@hytopia.com/server-protocol 1.0.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/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "@hytopia.com/server-protocol",
3
+ "version": "1.0.0",
4
+ "main": "index.js",
5
+ "scripts": {
6
+ "test": "echo \"Error: no test specified\" && exit 1"
7
+ },
8
+ "author": "",
9
+ "license": "ISC",
10
+ "devDependencies": {
11
+ "quicktype": "^23.0.170"
12
+ },
13
+ "dependencies": {
14
+ "ajv": "^8.17.1"
15
+ },
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/hytopiagg/server-protocol.git"
19
+ },
20
+ "bugs": {
21
+ "url": "https://github.com/hytopiagg/server-protocol/issues"
22
+ },
23
+ "homepage": "https://github.com/hytopiagg/server-protocol#readme",
24
+ "description": ""
25
+ }
@@ -0,0 +1,67 @@
1
+ import Ajv from '../shared/Ajv';
2
+ import type { JSONSchemaType, ValidateFunction } from 'ajv';
3
+
4
+ /*
5
+ * Packet types are numerically ordered relative to their use.
6
+ *
7
+ * Standard inbound/outbound packets must be within the id
8
+ * range of 0-127 to allow their numerical representation in
9
+ * msgpack'd format to be 1 byte - these packets are the most
10
+ * frequent and thus should be optimized for the smallest
11
+ * representation.
12
+ *
13
+ * Debug packets must be within the id range of 128-255 to
14
+ * allow their msgpack'd format to be 2 bytes - these packets
15
+ * are not packets used in typical or frequent gameplay and
16
+ * are acceptable to be represented with an additional byte
17
+ * in encoded format.
18
+ */
19
+
20
+ export enum PacketType {
21
+ // Standard Inbound Packet Types: 0 - 31 range
22
+ HEARTBEAT = 0,
23
+ INPUT = 1,
24
+
25
+ // Standard Outbound Packet Types: 32 - 127 range
26
+ BLOCK = 32,
27
+ BLOCK_REGISTRY = 33,
28
+ BLOCK_TYPE = 34,
29
+ CHUNK = 35,
30
+ ENTITY = 36,
31
+
32
+ // Debug Inbound Packet Types: 128 - 191 range
33
+ // NONE atm, start at 128
34
+
35
+ // Debug Outbound Packet Types: 192 - 255 range
36
+ PHYSICS_DEBUG_RENDER = 192
37
+ }
38
+
39
+ /*
40
+ * Generators for packet definitions and validators.
41
+ */
42
+
43
+ export interface IPacketDefinition<TId extends PacketType, TPayload> {
44
+ id: TId;
45
+ schema: JSONSchemaType<TPayload>;
46
+ validate: ValidateFunction<TPayload>;
47
+ }
48
+
49
+ export interface IPacket<TId extends PacketType, TPayload> {
50
+ id: TId;
51
+ payload: TPayload;
52
+ }
53
+
54
+ export const definePacket = <TId extends PacketType, TPayload>(
55
+ id: TId,
56
+ schema: JSONSchemaType<TPayload>,
57
+ ): IPacketDefinition<TId, TPayload> => ({
58
+ id,
59
+ schema,
60
+ validate: Ajv.instance.compile(schema),
61
+ });
62
+
63
+ // Represents any packet type
64
+ export type AnyPacket = IPacket<PacketType, unknown>;
65
+
66
+ // Represents any packet definition type
67
+ export type AnyPacketDefinition = IPacketDefinition<number, unknown>;
@@ -0,0 +1,11 @@
1
+ import { definePacket, PacketType } from '../PacketTypes';
2
+ import type { IPacket } from '../PacketTypes';
3
+ import { heartbeatSchema } from '../../schemas/Heartbeat';
4
+ import type { Heartbeat } from '../../schemas/Heartbeat';
5
+
6
+ export type HeartbeatPacket = IPacket<typeof PacketType.HEARTBEAT, Heartbeat>;
7
+
8
+ export const heartbeatPacketDefinition = definePacket(
9
+ PacketType.HEARTBEAT,
10
+ heartbeatSchema
11
+ );
@@ -0,0 +1,11 @@
1
+ import { definePacket, PacketType } from '../PacketTypes';
2
+ import type { IPacket } from '../PacketTypes';
3
+ import { inputSchema } from '../../schemas/Input';
4
+ import type { Input } from '../../schemas/Input';
5
+
6
+ export type InputPacket = IPacket<typeof PacketType.INPUT, Input>;
7
+
8
+ export const inputPacketDefinition = definePacket(
9
+ PacketType.INPUT,
10
+ inputSchema
11
+ );
@@ -0,0 +1,26 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+
4
+ const inboundPackets: Record<string, any> = {};
5
+ const outboundPackets: Record<string, any> = {};
6
+
7
+ const importPackets = async () => {
8
+ for (const directory of ['inbound', 'outbound']) {
9
+ const directoryPath = path.join(__dirname, directory);
10
+ const files = fs.readdirSync(directoryPath);
11
+
12
+ for (const file of files) {
13
+ if (file.endsWith('.ts')) {
14
+ const name = path.basename(file, '.ts');
15
+ const module = await import(`./${directory}/${name}`);
16
+ const typePackets = directory === 'inbound' ? inboundPackets : outboundPackets;
17
+ const lcfName = name.charAt(0).toLowerCase() + name.slice(1);
18
+ typePackets[`${lcfName}PacketDefinition`] = module[`${lcfName}PacketDefinition`];
19
+ }
20
+ }
21
+ }
22
+ };
23
+
24
+ await importPackets();
25
+
26
+ export default { inboundPackets, outboundPackets };
@@ -0,0 +1,11 @@
1
+ import { definePacket, PacketType } from '../PacketTypes';
2
+ import type { IPacket } from '../PacketTypes';
3
+ import { blockSchema } from '../../schemas/Block';
4
+ import type { Block } from '../../schemas/Block';
5
+
6
+ export type BlockPacket = IPacket<typeof PacketType.BLOCK, Block>;
7
+
8
+ export const blockPacketDefinition = definePacket(
9
+ PacketType.BLOCK,
10
+ blockSchema,
11
+ );
@@ -0,0 +1,11 @@
1
+ import { definePacket, PacketType } from '../PacketTypes';
2
+ import type { IPacket } from '../PacketTypes';
3
+ import { blockRegistrySchema } from '../../schemas/BlockRegistry';
4
+ import type { BlockRegistry } from '../../schemas/BlockRegistry';
5
+
6
+ export type BlockRegistryPacket = IPacket<typeof PacketType.BLOCK_REGISTRY, BlockRegistry>;
7
+
8
+ export const blockRegistryPacketDefinition = definePacket(
9
+ PacketType.BLOCK_REGISTRY,
10
+ blockRegistrySchema,
11
+ );
@@ -0,0 +1,11 @@
1
+ import { definePacket, PacketType } from '../PacketTypes';
2
+ import type { IPacket } from '../PacketTypes';
3
+ import { blockTypeSchema } from '../../schemas/BlockType';
4
+ import type { BlockType } from '../../schemas/BlockType';
5
+
6
+ export type BlockTypePacket = IPacket<typeof PacketType.BLOCK_TYPE, BlockType>;
7
+
8
+ export const blockTypePacketDefinition = definePacket(
9
+ PacketType.BLOCK_TYPE,
10
+ blockTypeSchema,
11
+ );
@@ -0,0 +1,11 @@
1
+ import { definePacket, PacketType } from '../PacketTypes';
2
+ import type { IPacket } from '../PacketTypes';
3
+ import { chunkSchema } from '../../schemas/Chunk';
4
+ import type { Chunk } from '../../schemas/Chunk';
5
+
6
+ export type ChunkPacket = IPacket<typeof PacketType.CHUNK, Chunk>;
7
+
8
+ export const chunkPacketDefinition = definePacket(
9
+ PacketType.CHUNK,
10
+ chunkSchema,
11
+ );
@@ -0,0 +1,11 @@
1
+ import { definePacket, PacketType } from '../PacketTypes';
2
+ import type { IPacket } from '../PacketTypes';
3
+ import { entitySchema } from '../../schemas/Entity';
4
+ import type { Entity } from '../../schemas/Entity';
5
+
6
+ export type EntityPacket = IPacket<typeof PacketType.ENTITY, Entity>;
7
+
8
+ export const entityPacketDefinition = definePacket(
9
+ PacketType.ENTITY,
10
+ entitySchema,
11
+ );
@@ -0,0 +1,11 @@
1
+ import { definePacket, PacketType } from '../PacketTypes';
2
+ import type { IPacket } from '../PacketTypes';
3
+ import { physicsDebugRenderSchema } from '../../schemas/PhysicsDebugRender';
4
+ import type { PhysicsDebugRender } from '../../schemas/PhysicsDebugRender';
5
+
6
+ export type PhysicsDebugRenderPacket = IPacket<typeof PacketType.PHYSICS_DEBUG_RENDER, PhysicsDebugRender>;
7
+
8
+ export const physicsDebugRenderPacketDefinition = definePacket(
9
+ PacketType.PHYSICS_DEBUG_RENDER,
10
+ physicsDebugRenderSchema,
11
+ );
@@ -0,0 +1,20 @@
1
+ import { vectorSchema } from './Vector';
2
+ import type { JSONSchemaType } from 'ajv';
3
+ import type { Vector } from './Vector';
4
+
5
+ export type Block = {
6
+ wi: string; // world id
7
+ i: number; // block id
8
+ c: Vector; // coordinate
9
+ };
10
+
11
+ export const blockSchema: JSONSchemaType<Block> = {
12
+ type: 'object',
13
+ properties: {
14
+ wi: { type: 'string' },
15
+ i: { type: 'number' },
16
+ c: vectorSchema,
17
+ },
18
+ required: [ 'wi', 'i', 'c' ],
19
+ additionalProperties: false,
20
+ }
@@ -0,0 +1,21 @@
1
+ import { blockTypeSchema } from './BlockType';
2
+ import type { JSONSchemaType } from 'ajv';
3
+ import type { BlockType } from './BlockType';
4
+
5
+ export type BlockRegistry = {
6
+ wi: string; // world id
7
+ b: BlockType[]; // block types
8
+ }
9
+
10
+ export const blockRegistrySchema: JSONSchemaType<BlockRegistry> = {
11
+ type: 'object',
12
+ properties: {
13
+ wi: { type: 'string' },
14
+ b: {
15
+ type: 'array',
16
+ items: blockTypeSchema,
17
+ }
18
+ },
19
+ required: [ 'wi', 'b' ],
20
+ additionalProperties: false,
21
+ }
@@ -0,0 +1,28 @@
1
+ import { colliderDescSchema } from './ColliderDesc';
2
+ import type { JSONSchemaType } from 'ajv';
3
+ import type { ColliderDesc } from './ColliderDesc';
4
+
5
+ export type BlockType = {
6
+ wi: string; // world id
7
+ i: number; // block type id
8
+ d: boolean; // delta
9
+ t?: string; // textureUri
10
+ n?: string; // name
11
+ c?: ColliderDesc; // collider desc
12
+ s?: boolean; // is solid
13
+ }
14
+
15
+ export const blockTypeSchema: JSONSchemaType<BlockType> = {
16
+ type: 'object',
17
+ properties: {
18
+ wi: { type: 'string' },
19
+ i: { type: 'number' },
20
+ d: { type: 'boolean' },
21
+ t: { type: 'string', nullable: true },
22
+ n: { type: 'string', nullable: true },
23
+ c: { ...colliderDescSchema, nullable: true },
24
+ s: { type: 'boolean', nullable: true },
25
+ },
26
+ required: [ 'wi', 'i', 'd' ],
27
+ additionalProperties: false,
28
+ }
@@ -0,0 +1,29 @@
1
+ import { vectorSchema } from './Vector';
2
+ import type { JSONSchemaType } from 'ajv';
3
+ import type { Vector } from './Vector';
4
+
5
+ export type Chunk = {
6
+ wi: string; // world id
7
+ c: Vector; // chunk coordinate [ 16n, 16n, 16n ]
8
+ b: Uint8Array | number[]; // block ids in chunk, 16^3 entries, registry block ids 1-255, 0 = none
9
+ };
10
+
11
+ export const chunkSchema: JSONSchemaType<Chunk> = {
12
+ type: 'object',
13
+ properties: {
14
+ wi: { type: 'string' },
15
+ c: vectorSchema,
16
+ b: {
17
+ type: 'array',
18
+ items: {
19
+ type: 'number',
20
+ minimum: 0,
21
+ maximum: 255
22
+ },
23
+ minItems: 4096,
24
+ maxItems: 4096
25
+ }
26
+ },
27
+ required: [ 'wi', 'c', 'b' ],
28
+ additionalProperties: false,
29
+ }
@@ -0,0 +1,16 @@
1
+ import { colliderDescSchema } from './ColliderDesc';
2
+ import type { JSONSchemaType } from 'ajv';
3
+ import type { ColliderDesc } from './ColliderDesc';
4
+
5
+ export type Collider = {
6
+ h?: number; // collider handle for rapier
7
+ } & ColliderDesc;
8
+
9
+ export const colliderSchema: JSONSchemaType<Collider> = {
10
+ type: 'object',
11
+ properties: {
12
+ h: { type: 'number', nullable: true },
13
+ ...colliderDescSchema.properties
14
+ },
15
+ additionalProperties: false,
16
+ }
@@ -0,0 +1,47 @@
1
+ import { shapeSchema } from './Shape';
2
+ import { quaternionSchema } from './Quaternion';
3
+ import { vectorSchema } from './Vector';
4
+ import type { JSONSchemaType } from 'ajv';
5
+ import type { Shape } from './Shape';
6
+ import type { Quaternion } from './Quaternion';
7
+ import type { Vector } from './Vector';
8
+
9
+ export type ColliderDesc = {
10
+ at?: number; // active collision types
11
+ cg?: number; // collision groups
12
+ cs?: number; // contact skin
13
+ d?: number; // density
14
+ f?: number; // friction
15
+ fr?: number; // friction combine rule
16
+ en?: boolean; // enabled
17
+ sn?: boolean; // sensor
18
+ m?: number; // mass
19
+ re?: number; // restitution
20
+ rer?: number; // restitution combine rule
21
+ r?: Quaternion; // rotation
22
+ s?: Shape; // shape
23
+ sg?: number; // solver groups
24
+ t?: Vector; // translation
25
+ }
26
+
27
+ export const colliderDescSchema: JSONSchemaType<ColliderDesc> = {
28
+ type: 'object',
29
+ properties: {
30
+ at: { type: 'number', nullable: true },
31
+ cg: { type: 'number', nullable: true },
32
+ cs: { type: 'number', nullable: true },
33
+ d: { type: 'number', nullable: true },
34
+ f: { type: 'number', nullable: true },
35
+ fr: { type: 'number', nullable: true },
36
+ en: { type: 'boolean', nullable: true },
37
+ sn: { type: 'boolean', nullable: true },
38
+ m: { type: 'number', nullable: true },
39
+ re: { type: 'number', nullable: true },
40
+ rer: { type: 'number', nullable: true },
41
+ r: { ...quaternionSchema, nullable: true },
42
+ s: { ...shapeSchema, nullable: true },
43
+ sg: { type: 'number', nullable: true },
44
+ t: { ...vectorSchema, nullable: true },
45
+ },
46
+ additionalProperties: false,
47
+ }
@@ -0,0 +1,30 @@
1
+ import { rigidBodySchema } from './RigidBody';
2
+ import type { JSONSchemaType } from 'ajv';
3
+ import type { RigidBody } from './RigidBody';
4
+
5
+ export type Entity = {
6
+ wi: string; // world id
7
+ i: number; // entity id
8
+ d: boolean; // delta
9
+ f?: boolean; // focused
10
+ n?: string; // name
11
+ m?: string; // model uri
12
+ t?: string; // texture uri
13
+ r?: RigidBody; // rigid body
14
+ }
15
+
16
+ export const entitySchema: JSONSchemaType<Entity> = {
17
+ type: 'object',
18
+ properties: {
19
+ wi: { type: 'string' },
20
+ i: { type: 'number' },
21
+ d: { type: 'boolean' },
22
+ f: { type: 'boolean', nullable: true },
23
+ n: { type: 'string', nullable: true },
24
+ m: { type: 'string', nullable: true },
25
+ t: { type: 'string', nullable: true },
26
+ r: { ...rigidBodySchema, nullable: true },
27
+ },
28
+ required: [ 'wi', 'i', 'd' ],
29
+ additionalProperties: false,
30
+ }
@@ -0,0 +1,8 @@
1
+ import type { JSONSchemaType } from 'ajv';
2
+
3
+ export type Heartbeat = null;
4
+
5
+ export const heartbeatSchema: JSONSchemaType<Heartbeat> = {
6
+ type: 'null',
7
+ nullable: true,
8
+ }
@@ -0,0 +1,35 @@
1
+ import { JSONSchemaType } from 'ajv';
2
+
3
+ export type Input = {
4
+ w?: boolean; // w key pressed
5
+ a?: boolean; // a key pressed
6
+ s?: boolean; // s key pressed
7
+ d?: boolean; // d key pressed
8
+ sp?: boolean; // space key pressed
9
+ sh?: boolean; // shift key pressed
10
+ ct?: boolean; // ctrl key pressed
11
+ tb?: boolean; // tab key pressed
12
+ ml?: boolean; // mouse left pressed
13
+ mr?: boolean; // mouse right pressed
14
+ cp?: number; // camera pitch radians
15
+ cy?: number; // camera yaw radians
16
+ }
17
+
18
+ export const inputSchema: JSONSchemaType<Input> = {
19
+ type: 'object',
20
+ properties: {
21
+ w: { type: 'boolean', nullable: true },
22
+ a: { type: 'boolean', nullable: true },
23
+ s: { type: 'boolean', nullable: true },
24
+ d: { type: 'boolean', nullable: true },
25
+ sp: { type: 'boolean', nullable: true },
26
+ sh: { type: 'boolean', nullable: true },
27
+ ct: { type: 'boolean', nullable: true },
28
+ tb: { type: 'boolean', nullable: true },
29
+ ml: { type: 'boolean', nullable: true },
30
+ mr: { type: 'boolean', nullable: true },
31
+ cp: { type: 'number', nullable: true },
32
+ cy: { type: 'number', nullable: true },
33
+ },
34
+ additionalProperties: false,
35
+ }
@@ -0,0 +1,18 @@
1
+ import { JSONSchemaType } from 'ajv';
2
+
3
+ export type PhysicsDebugRender = {
4
+ wi: number; // world id
5
+ v: number[]; // rapier debug render vertices
6
+ c: number[]; // rapier debug render colors
7
+ }
8
+
9
+ export const physicsDebugRenderSchema: JSONSchemaType<PhysicsDebugRender> = {
10
+ type: 'object',
11
+ properties: {
12
+ wi: { type: 'number' },
13
+ v: { type: 'array', items: { type: 'number' } },
14
+ c: { type: 'array', items: { type: 'number' } },
15
+ },
16
+ required: [ 'wi', 'v', 'c' ],
17
+ additionalProperties: false,
18
+ }
@@ -0,0 +1,20 @@
1
+ import type { JSONSchemaType } from 'ajv';
2
+
3
+ export type Quaternion = [
4
+ number, // x
5
+ number, // y
6
+ number, // z
7
+ number, // w
8
+ ];
9
+
10
+ export const quaternionSchema: JSONSchemaType<Quaternion> = {
11
+ type: 'array',
12
+ items: [
13
+ { type: 'number' },
14
+ { type: 'number' },
15
+ { type: 'number' },
16
+ { type: 'number' }
17
+ ],
18
+ minItems: 4,
19
+ maxItems: 4
20
+ }
@@ -0,0 +1,16 @@
1
+ import { rigidBodyDescSchema } from './RigidBodyDesc';
2
+ import type { JSONSchemaType } from 'ajv';
3
+ import { RigidBodyDesc } from './RigidBodyDesc';
4
+
5
+ export type RigidBody = {
6
+ h?: number; // rigid body handle for rapier
7
+ } & RigidBodyDesc;
8
+
9
+ export const rigidBodySchema: JSONSchemaType<RigidBody> = {
10
+ type: 'object',
11
+ properties: {
12
+ handle: { type: 'number', nullable: true },
13
+ ...rigidBodyDescSchema.properties,
14
+ },
15
+ additionalProperties: false,
16
+ }
@@ -0,0 +1,47 @@
1
+ import { colliderSchema } from './Collider';
2
+ import { vectorSchema } from './Vector';
3
+ import { vectorBooleanSchema } from './VectorBoolean'
4
+ import type { JSONSchemaType } from 'ajv';
5
+ import type { Collider } from './Collider';
6
+ import type { Vector } from './Vector';
7
+ import type { VectorBoolean } from './VectorBoolean';
8
+
9
+ export type RigidBodyDesc = {
10
+ ad?: number; // angular dampening
11
+ av?: Vector; // angular velocity
12
+ b?: number; // body type
13
+ c?: Collider[]; // colliders
14
+ d?: number; // dominance group
15
+ ce?: boolean; // ccd enabled
16
+ en?: boolean; // enabled
17
+ er?: VectorBoolean; // enabled rotations
18
+ et?: VectorBoolean; // enabled translations
19
+ g?: number; // gravity scaling
20
+ ld?: number; // linear damping
21
+ lv?: Vector; // linear velocity
22
+ sl?: boolean; // sleeping
23
+ scp?: number; // soft ccd prediction
24
+ t?: Vector; // translation
25
+ }
26
+
27
+ export const rigidBodyDescSchema: JSONSchemaType<RigidBodyDesc> = {
28
+ type: 'object',
29
+ properties: {
30
+ ad: { type: 'number', nullable: true },
31
+ av: { ...vectorSchema, nullable: true },
32
+ b: { type: 'number', nullable: true },
33
+ c: { type: 'array', items: colliderSchema, nullable: true },
34
+ d: { type: 'number', nullable: true },
35
+ ce: { type: 'boolean', nullable: true },
36
+ en: { type: 'boolean', nullable: true },
37
+ er: { ...vectorBooleanSchema, nullable: true },
38
+ et: { ...vectorBooleanSchema, nullable: true },
39
+ g: { type: 'number', nullable: true },
40
+ ld: { type: 'number', nullable: true },
41
+ lv: { ...vectorSchema, nullable: true },
42
+ sl: { type: 'boolean', nullable: true },
43
+ scp: { type: 'number', nullable: true },
44
+ t: { ...vectorSchema, nullable: true },
45
+ },
46
+ additionalProperties: false,
47
+ }
@@ -0,0 +1,23 @@
1
+ import { JSONSchemaType } from 'ajv';
2
+ import { vectorSchema } from './Vector';
3
+ import type { Vector } from './Vector';
4
+
5
+ export type Shape = {
6
+ t?: number; // type
7
+ b?: number; // border radius
8
+ r?: number; // radius
9
+ hx?: Vector; // half extends
10
+ hh?: number; // half height
11
+ }
12
+
13
+ export const shapeSchema: JSONSchemaType<Shape> = {
14
+ type: 'object',
15
+ properties: {
16
+ t: { type: 'number', nullable: true },
17
+ b: { type: 'number', nullable: true },
18
+ r: { type: 'number', nullable: true },
19
+ hx: { ...vectorSchema, nullable: true },
20
+ hh: { type: 'number', nullable: true },
21
+ },
22
+ additionalProperties: false,
23
+ }
@@ -0,0 +1,18 @@
1
+ import type { JSONSchemaType } from 'ajv';
2
+
3
+ export type Vector = [
4
+ number, // x
5
+ number, // y
6
+ number, // z
7
+ ];
8
+
9
+ export const vectorSchema: JSONSchemaType<Vector> = {
10
+ type: 'array',
11
+ items: [
12
+ { type: 'number' },
13
+ { type: 'number' },
14
+ { type: 'number' }
15
+ ],
16
+ minItems: 3,
17
+ maxItems: 3
18
+ }
@@ -0,0 +1,18 @@
1
+ import { JSONSchemaType } from 'ajv';
2
+
3
+ export type VectorBoolean = [
4
+ boolean, // x
5
+ boolean, // y
6
+ boolean, // z
7
+ ];
8
+
9
+ export const vectorBooleanSchema: JSONSchemaType<VectorBoolean> = {
10
+ type: 'array',
11
+ items: [
12
+ { type: 'boolean' },
13
+ { type: 'boolean' },
14
+ { type: 'boolean' }
15
+ ],
16
+ minItems: 3,
17
+ maxItems: 3
18
+ }
package/shared/Ajv.ts ADDED
@@ -0,0 +1,8 @@
1
+ import Ajv from "ajv";
2
+
3
+ export default class extends Ajv {
4
+ // shared instance for the entire application
5
+ // per ajv docs, this is for performance around
6
+ // compiled validate function caching & more.
7
+ public static readonly instance = new Ajv();
8
+ }