Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More โ†’
Socket
Sign inDemoInstall
Socket

bitecs

Package Overview
Dependencies
Maintainers
1
Versions
133
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

bitecs - npm Package Compare versions

Comparing version 0.3.6 to 0.3.7

42

dist/index.d.ts

@@ -38,3 +38,3 @@ declare module 'bitecs' {

type ArrayByType = {
export type ArrayByType = {
['bool']: boolean[];

@@ -52,47 +52,49 @@ [Types.i8]: Int8Array;

type ComponentType<T extends ISchema> = {
export type ComponentType<T extends ISchema> = {
[key in keyof T]: T[key] extends Type ? ArrayByType[T[key]] : T[key] extends ISchema ? ComponentType<T[key]> : unknown;
}
interface IWorld {
export interface IWorld {
[key: string]: any
}
interface ISchema {
export interface ISchema {
[key: string]: Type | ISchema
}
interface IComponentProp {
export interface IComponentProp {
[key: string]: TypedArray
}
interface IComponent {
export interface IComponent {
[key: string]: TypedArray | IComponentProp
}
type QueryModifier = (c: (IComponent | IComponentProp)[]) => (world: IWorld) => IComponent | QueryModifier
export type Component = IComponent | ComponentType<ISchema>
type Query = (world: IWorld) => number[]
export type QueryModifier = (c: (IComponent | IComponentProp)[]) => (world: IWorld) => IComponent | QueryModifier
type System = (world: IWorld) => void
export type Query = (world: IWorld) => number[]
export type System = (world: IWorld) => IWorld
export function createWorld(size?: number): IWorld
export function addEntity(world: IWorld): number
export function removeEntity(world: IWorld, eid: number): void
export function registerComponent(world: IWorld, component: IComponent): void
export function registerComponents(world: IWorld, components: IComponent[]): void
export function registerComponent(world: IWorld, component: Component): void
export function registerComponents(world: IWorld, components: Component[]): void
export function defineComponent<T extends ISchema>(schema?: T): ComponentType<T>
export function addComponent(world: IWorld, component: IComponent, eid: number): void
export function removeComponent(world: IWorld, component: IComponent, eid: number): void
export function hasComponent(world: IWorld, component: IComponent, eid: number): boolean
export function defineQuery(components: (ComponentType<ISchema> | IComponent | QueryModifier)[]): Query
export function Changed(c: ComponentType<ISchema>): IComponent | QueryModifier
export function Not(c: ComponentType<ISchema>): IComponent | QueryModifier
export function addComponent(world: IWorld, component: Component, eid: number): void
export function removeComponent(world: IWorld, component: Component, eid: number): void
export function hasComponent(world: IWorld, component: Component, eid: number): boolean
export function defineQuery(components: (Component | QueryModifier)[]): Query
export function Changed(c: Component): Component | QueryModifier
export function Not(c: Component): Component | QueryModifier
export function enterQuery(query: Query): Query
export function exitQuery(query: Query): Query
export function commitRemovals(world: IWorld): void
export function defineSystem(update: (world: IWorld) => void): System
export function defineSerializer(target: IWorld | IComponent | IComponentProp | QueryModifier, maxBytes?: number): (target: IWorld | number[]) => ArrayBuffer
export function defineDeserializer(target: IWorld | IComponent | IComponentProp | QueryModifier): (world: IWorld, packet: ArrayBuffer) => void
export function defineSystem(update: (world: IWorld, ...args: any[]) => IWorld): System
export function defineSerializer(target: IWorld | Component | IComponentProp | QueryModifier, maxBytes?: number): (target: IWorld | number[]) => ArrayBuffer
export function defineDeserializer(target: IWorld | Component | IComponentProp | QueryModifier): (world: IWorld, packet: ArrayBuffer) => void
export function pipe(...fns: ((...args: any[]) => any)[]): (input: any) => any
}

@@ -51,2 +51,3 @@ const TYPES_ENUM = {

const $subarray = Symbol('subarray');
const $parentArray = Symbol('subStore');
const $tagStore = Symbol('tagStore');

@@ -83,2 +84,3 @@ const $queryShadow = Symbol('queryShadow');

array[$indexBytes] = TYPES[indexType].BYTES_PER_ELEMENT;
const start = cursors[type];
let end = 0;

@@ -101,2 +103,3 @@

cursors[type] = end;
store[$parentArray] = metadata[$storeSubarrays][type].subarray(start, end);
};

@@ -129,3 +132,3 @@

});
resizeRecursive(store, store, size); // resizeSubarrays(store, size)
resizeRecursive(store, store, size);
};

@@ -146,2 +149,4 @@ const resetStoreFor = (store, eid) => {

const contiguousArray = store => store[$parentArray];
const createArrayStore = (metadata, type, length) => {

@@ -172,2 +177,3 @@ const size = metadata[$storeSize];

const start = cursors[type];
let end = 0;

@@ -190,2 +196,3 @@

cursors[type] = end;
store[$parentArray] = metadata[$storeSubarrays][type].subarray(start, end);
return store;

@@ -259,5 +266,5 @@ };

metadata[$storeFlattened].push(a[k]); // Object.freeze(a[k])
metadata[$storeFlattened].push(a[k]); // Object.seal(a[k])
} else if (a[k] instanceof Object) {
a[k] = Object.keys(a[k]).reduce(recursiveTransform, a[k]); // Object.freeze(a[k])
a[k] = Object.keys(a[k]).reduce(recursiveTransform, a[k]); // Object.seal(a[k])
}

@@ -270,3 +277,3 @@

stores[$store][$storeBase] = () => stores[$store]; // Object.freeze(stores[$store])
stores[$store][$storeBase] = () => stores[$store]; // Object.seal(stores[$store])

@@ -417,6 +424,9 @@

};
const newEntities = new Map();
const defineDeserializer = target => {
const isWorld = Object.getOwnPropertySymbols(target).includes($componentMap);
let [componentProps] = canonicalize(target);
return (world, packet) => {
return (world, packet, overwrite = true) => {
newEntities.clear();
if (resized) {

@@ -436,3 +446,2 @@ [componentProps] = canonicalize(target);

let where = 0;
const newEntities = new Map();

@@ -456,6 +465,6 @@ while (where < packet.byteLength) {

eid = newEid;
} // if this world hasn't seen this eid yet
} // if this world hasn't seen this eid yet, or if not overwriting
if (!world[$entityEnabled][eid]) {
if (!world[$entityEnabled][eid] || !overwrite) {
// make a new entity for the data

@@ -504,3 +513,3 @@ const newEid = addEntity(world);

const NONE$1 = 2 ** 32;
const defaultSize = 100000; // need a global EID cursor which all worlds and all components know about
let defaultSize = 100000; // need a global EID cursor which all worlds and all components know about
// so that world entities can posess entire rows spanning all component tables

@@ -516,2 +525,12 @@

const removed = [];
const resetGlobals = () => {
globalSize = defaultSize;
globalEntityCursor = 0;
removed.length = 0;
};
const getDefaultSize = () => defaultSize;
const setDefaultSize = x => {
defaultSize = x;
resetGlobals();
};
const getEntityCursor = () => globalEntityCursor;

@@ -678,4 +697,4 @@ const eidToWorld = new Map();

const diff = q => {
q.changed.length = 0;
const diff = (q, clearDiff) => {
if (clearDiff) q.changed.length = 0;
const flat = q.flatProps;

@@ -712,8 +731,11 @@

const defineQuery = components => {
const query = function (world) {
if (components === undefined || components[$componentMap] !== undefined) {
return world => world ? world[$entityArray] : components[$entityArray];
}
const query = function (world, clearDiff = true) {
if (!world[$queryMap].has(query)) registerQuery(world, query);
const q = world[$queryMap].get(query); // queryHooks(q)
const q = world[$queryMap].get(query);
queryCommitRemovals(world, q);
if (q.changedComponents.length) return diff(q);
if (q.changedComponents.length) return diff(q, clearDiff);
return q.entities;

@@ -799,3 +821,3 @@ };

q.toRemove.push(eid);
world[$dirtyQueries].add(q); // TODO: pop swap so dupes don't enter
world[$dirtyQueries].add(q); // TODO: pop swap so dupes don't enter (in the case where an EID is removed twice before query is called again)

@@ -811,3 +833,3 @@ q.exited.push(eid);

const defineComponent = schema => {
const component = createStore(schema, defaultSize);
const component = createStore(schema, getDefaultSize());
if (schema && Object.keys(schema).length) components.push(component);

@@ -851,2 +873,8 @@ return component;

const addComponent = (world, component, eid, reset = false) => {
if (!Number.isInteger(eid)) {
component = world;
world = eidToWorld.get(eid);
reset = eid || reset;
}
if (!world[$componentMap].has(component)) registerComponent(world, component);

@@ -870,2 +898,8 @@ if (hasComponent(world, component, eid)) return; // Add bitflag to entity bitmask

const removeComponent = (world, component, eid, reset = true) => {
if (!Number.isInteger(eid)) {
component = world;
world = eidToWorld.get(eid);
reset = eid || reset;
}
const {

@@ -932,9 +966,9 @@ generationId,

const system = world => {
const system = (world, ...args) => {
if (create && !init.has(world)) {
create(world);
create(world, ...args);
init.add(world);
}
update(world);
update(world, ...args);
commitRemovals(world);

@@ -960,3 +994,4 @@ return world;

if (Array.isArray(tmp)) {
tmp = tmp.reduce((a, v) => a.concat(fn(v)), []);
// tmp = tmp.reduce((a,v) => a.concat(fn(v)),[])
tmp = fn(...tmp);
} else {

@@ -971,3 +1006,3 @@ tmp = fn(tmp);

export { Changed, Not, Types, addComponent, addEntity, commitRemovals, createWorld, defineComponent, defineDeserializer, defineQuery, defineSerializer, defineSystem, enterQuery, exitQuery, hasComponent, pipe, registerComponent, registerComponents, removeComponent, removeEntity };
export { Changed, Not, Types, addComponent, addEntity, commitRemovals, contiguousArray, createWorld, defineComponent, defineDeserializer, defineQuery, defineSerializer, defineSystem, enterQuery, exitQuery, hasComponent, pipe, registerComponent, registerComponents, removeComponent, removeEntity, setDefaultSize };
//# sourceMappingURL=index.es.js.map

@@ -55,2 +55,3 @@ 'use strict';

const $subarray = Symbol('subarray');
const $parentArray = Symbol('subStore');
const $tagStore = Symbol('tagStore');

@@ -87,2 +88,3 @@ const $queryShadow = Symbol('queryShadow');

array[$indexBytes] = TYPES[indexType].BYTES_PER_ELEMENT;
const start = cursors[type];
let end = 0;

@@ -105,2 +107,3 @@

cursors[type] = end;
store[$parentArray] = metadata[$storeSubarrays][type].subarray(start, end);
};

@@ -133,3 +136,3 @@

});
resizeRecursive(store, store, size); // resizeSubarrays(store, size)
resizeRecursive(store, store, size);
};

@@ -150,2 +153,4 @@ const resetStoreFor = (store, eid) => {

const contiguousArray = store => store[$parentArray];
const createArrayStore = (metadata, type, length) => {

@@ -176,2 +181,3 @@ const size = metadata[$storeSize];

const start = cursors[type];
let end = 0;

@@ -194,2 +200,3 @@

cursors[type] = end;
store[$parentArray] = metadata[$storeSubarrays][type].subarray(start, end);
return store;

@@ -263,5 +270,5 @@ };

metadata[$storeFlattened].push(a[k]); // Object.freeze(a[k])
metadata[$storeFlattened].push(a[k]); // Object.seal(a[k])
} else if (a[k] instanceof Object) {
a[k] = Object.keys(a[k]).reduce(recursiveTransform, a[k]); // Object.freeze(a[k])
a[k] = Object.keys(a[k]).reduce(recursiveTransform, a[k]); // Object.seal(a[k])
}

@@ -274,3 +281,3 @@

stores[$store][$storeBase] = () => stores[$store]; // Object.freeze(stores[$store])
stores[$store][$storeBase] = () => stores[$store]; // Object.seal(stores[$store])

@@ -421,6 +428,9 @@

};
const newEntities = new Map();
const defineDeserializer = target => {
const isWorld = Object.getOwnPropertySymbols(target).includes($componentMap);
let [componentProps] = canonicalize(target);
return (world, packet) => {
return (world, packet, overwrite = true) => {
newEntities.clear();
if (resized) {

@@ -440,3 +450,2 @@ [componentProps] = canonicalize(target);

let where = 0;
const newEntities = new Map();

@@ -460,6 +469,6 @@ while (where < packet.byteLength) {

eid = newEid;
} // if this world hasn't seen this eid yet
} // if this world hasn't seen this eid yet, or if not overwriting
if (!world[$entityEnabled][eid]) {
if (!world[$entityEnabled][eid] || !overwrite) {
// make a new entity for the data

@@ -508,3 +517,3 @@ const newEid = addEntity(world);

const NONE$1 = 2 ** 32;
const defaultSize = 100000; // need a global EID cursor which all worlds and all components know about
let defaultSize = 100000; // need a global EID cursor which all worlds and all components know about
// so that world entities can posess entire rows spanning all component tables

@@ -520,2 +529,12 @@

const removed = [];
const resetGlobals = () => {
globalSize = defaultSize;
globalEntityCursor = 0;
removed.length = 0;
};
const getDefaultSize = () => defaultSize;
const setDefaultSize = x => {
defaultSize = x;
resetGlobals();
};
const getEntityCursor = () => globalEntityCursor;

@@ -682,4 +701,4 @@ const eidToWorld = new Map();

const diff = q => {
q.changed.length = 0;
const diff = (q, clearDiff) => {
if (clearDiff) q.changed.length = 0;
const flat = q.flatProps;

@@ -716,8 +735,11 @@

const defineQuery = components => {
const query = function (world) {
if (components === undefined || components[$componentMap] !== undefined) {
return world => world ? world[$entityArray] : components[$entityArray];
}
const query = function (world, clearDiff = true) {
if (!world[$queryMap].has(query)) registerQuery(world, query);
const q = world[$queryMap].get(query); // queryHooks(q)
const q = world[$queryMap].get(query);
queryCommitRemovals(world, q);
if (q.changedComponents.length) return diff(q);
if (q.changedComponents.length) return diff(q, clearDiff);
return q.entities;

@@ -803,3 +825,3 @@ };

q.toRemove.push(eid);
world[$dirtyQueries].add(q); // TODO: pop swap so dupes don't enter
world[$dirtyQueries].add(q); // TODO: pop swap so dupes don't enter (in the case where an EID is removed twice before query is called again)

@@ -815,3 +837,3 @@ q.exited.push(eid);

const defineComponent = schema => {
const component = createStore(schema, defaultSize);
const component = createStore(schema, getDefaultSize());
if (schema && Object.keys(schema).length) components.push(component);

@@ -855,2 +877,8 @@ return component;

const addComponent = (world, component, eid, reset = false) => {
if (!Number.isInteger(eid)) {
component = world;
world = eidToWorld.get(eid);
reset = eid || reset;
}
if (!world[$componentMap].has(component)) registerComponent(world, component);

@@ -874,2 +902,8 @@ if (hasComponent(world, component, eid)) return; // Add bitflag to entity bitmask

const removeComponent = (world, component, eid, reset = true) => {
if (!Number.isInteger(eid)) {
component = world;
world = eidToWorld.get(eid);
reset = eid || reset;
}
const {

@@ -936,9 +970,9 @@ generationId,

const system = world => {
const system = (world, ...args) => {
if (create && !init.has(world)) {
create(world);
create(world, ...args);
init.add(world);
}
update(world);
update(world, ...args);
commitRemovals(world);

@@ -964,3 +998,4 @@ return world;

if (Array.isArray(tmp)) {
tmp = tmp.reduce((a, v) => a.concat(fn(v)), []);
// tmp = tmp.reduce((a,v) => a.concat(fn(v)),[])
tmp = fn(...tmp);
} else {

@@ -981,2 +1016,3 @@ tmp = fn(tmp);

exports.commitRemovals = commitRemovals;
exports.contiguousArray = contiguousArray;
exports.createWorld = createWorld;

@@ -996,2 +1032,3 @@ exports.defineComponent = defineComponent;

exports.removeEntity = removeEntity;
exports.setDefaultSize = setDefaultSize;
//# sourceMappingURL=index.js.map

@@ -38,3 +38,3 @@ declare module 'bitecs' {

type ArrayByType = {
export type ArrayByType = {
['bool']: boolean[];

@@ -52,47 +52,49 @@ [Types.i8]: Int8Array;

type ComponentType<T extends ISchema> = {
export type ComponentType<T extends ISchema> = {
[key in keyof T]: T[key] extends Type ? ArrayByType[T[key]] : T[key] extends ISchema ? ComponentType<T[key]> : unknown;
}
interface IWorld {
export interface IWorld {
[key: string]: any
}
interface ISchema {
export interface ISchema {
[key: string]: Type | ISchema
}
interface IComponentProp {
export interface IComponentProp {
[key: string]: TypedArray
}
interface IComponent {
export interface IComponent {
[key: string]: TypedArray | IComponentProp
}
type QueryModifier = (c: (IComponent | IComponentProp)[]) => (world: IWorld) => IComponent | QueryModifier
export type Component = IComponent | ComponentType<ISchema>
type Query = (world: IWorld) => number[]
export type QueryModifier = (c: (IComponent | IComponentProp)[]) => (world: IWorld) => IComponent | QueryModifier
type System = (world: IWorld) => void
export type Query = (world: IWorld) => number[]
export type System = (world: IWorld) => IWorld
export function createWorld(size?: number): IWorld
export function addEntity(world: IWorld): number
export function removeEntity(world: IWorld, eid: number): void
export function registerComponent(world: IWorld, component: IComponent): void
export function registerComponents(world: IWorld, components: IComponent[]): void
export function registerComponent(world: IWorld, component: Component): void
export function registerComponents(world: IWorld, components: Component[]): void
export function defineComponent<T extends ISchema>(schema?: T): ComponentType<T>
export function addComponent(world: IWorld, component: IComponent, eid: number): void
export function removeComponent(world: IWorld, component: IComponent, eid: number): void
export function hasComponent(world: IWorld, component: IComponent, eid: number): boolean
export function defineQuery(components: (ComponentType<ISchema> | IComponent | QueryModifier)[]): Query
export function Changed(c: ComponentType<ISchema>): IComponent | QueryModifier
export function Not(c: ComponentType<ISchema>): IComponent | QueryModifier
export function addComponent(world: IWorld, component: Component, eid: number): void
export function removeComponent(world: IWorld, component: Component, eid: number): void
export function hasComponent(world: IWorld, component: Component, eid: number): boolean
export function defineQuery(components: (Component | QueryModifier)[]): Query
export function Changed(c: Component): Component | QueryModifier
export function Not(c: Component): Component | QueryModifier
export function enterQuery(query: Query): Query
export function exitQuery(query: Query): Query
export function commitRemovals(world: IWorld): void
export function defineSystem(update: (world: IWorld) => void): System
export function defineSerializer(target: IWorld | IComponent | IComponentProp | QueryModifier, maxBytes?: number): (target: IWorld | number[]) => ArrayBuffer
export function defineDeserializer(target: IWorld | IComponent | IComponentProp | QueryModifier): (world: IWorld, packet: ArrayBuffer) => void
export function defineSystem(update: (world: IWorld, ...args: any[]) => IWorld): System
export function defineSerializer(target: IWorld | Component | IComponentProp | QueryModifier, maxBytes?: number): (target: IWorld | number[]) => ArrayBuffer
export function defineDeserializer(target: IWorld | Component | IComponentProp | QueryModifier): (world: IWorld, packet: ArrayBuffer) => void
export function pipe(...fns: ((...args: any[]) => any)[]): (input: any) => any
}
{
"name": "bitecs",
"version": "0.3.6",
"version": "0.3.7",
"description": "Functional, minimal, data-driven, ultra-high performance ECS library written in Javascript",
"license": "MPL-2.0",
"type": "module",
"main": "./dist/index.min.js",
"main": "./dist/index.js",
"module": "./dist/index.es.js",

@@ -12,3 +12,3 @@ "types": "./dist/index.d.ts",

"import": "./dist/index.es.js",
"require": "./dist/index.min.js"
"require": "./dist/index.js"
},

@@ -15,0 +15,0 @@ "author": {

@@ -5,3 +5,8 @@ # ๐Ÿ‘พ bitECS ๐Ÿ‘พ [![npm](https://img.shields.io/npm/v/bitecs.svg)](https://www.npmjs.com/package/bitecs) [![Minzipped](https://badgen.net/bundlephobia/minzip/bitecs)](https://www.npmjs.com/package/bitecs) [![npm](https://img.shields.io/npm/dt/bitecs.svg)](https://www.npmjs.com/package/bitecs) [![License](https://badgen.net/npm/license/bitecs)](https://www.npmjs.com/package/bitecs)

Used in:
- [ModNGN]()
- [Phaser 4]()
- [XREngine]()
## โœจ Features

@@ -65,2 +70,3 @@

pipe,
Types

@@ -179,3 +185,3 @@ } from 'bitecs'

Systems are functions and are run against a world to update componenet state of entities, or anything else.
Systems are functions and are run against a world to update component state of entities, or anything else.

@@ -182,0 +188,0 @@ Queries are used inside of systems to obtain a relevant set of entities and perform operations on their component data.

import assert, { strictEqual } from 'assert'
import { defaultSize } from '../../src/Entity.js'
import { getDefaultSize } from '../../src/Entity.js'
import { Types } from '../../src/index.js'
import { createStore, TYPES } from '../../src/Storage.js'
import { createStore, resizeStore, TYPES } from '../../src/Storage.js'
let defaultSize = getDefaultSize()
const arraysEqual = (a,b) => !!a && !!b && !(a<b || b<a)
describe('Storage Integration Tests', () => {
it('should default to size of 100k', () => {
it('should default to size of ' + defaultSize, () => {
const store = createStore({ value: Types.i8 }, defaultSize)

@@ -42,2 +44,24 @@ strictEqual(store.value.length, defaultSize)

})
it('should resize arrays of ' + type, () => {
const store = createStore({ array: [type, 4] }, 3)
store.array[0].set([1,2,3,4])
store.array[1].set([5,6,7,8])
store.array[2].set([9,10,11,12])
strictEqual(store.array[3], undefined)
resizeStore(store, 6)
assert(store.array[5] !== undefined)
strictEqual(store.array[6], undefined)
assert(arraysEqual(Array.from(store.array[0]), [1,2,3,4]))
assert(arraysEqual(Array.from(store.array[1]), [5,6,7,8]))
assert(arraysEqual(Array.from(store.array[2]), [9,10,11,12]))
assert(arraysEqual(Array.from(store.array[3]), [0,0,0,0]))
assert(arraysEqual(Array.from(store.array[4]), [0,0,0,0]))
assert(arraysEqual(Array.from(store.array[5]), [0,0,0,0]))
})
})

@@ -44,0 +68,0 @@ it('should create flat stores', () => {

import assert, { strictEqual } from 'assert'
import { $componentMap } from '../../src/Component.js'
import { $entityEnabled, $entityMasks, resetGlobals, addEntity, defaultSize } from '../../src/Entity.js'
import { $entityEnabled, $entityMasks, resetGlobals, addEntity, getDefaultSize } from '../../src/Entity.js'
import { $dirtyQueries, $queries, $queryMap } from '../../src/Query.js'
import { createWorld, $size, $bitflag } from '../../src/World.js'
const defaultSize = getDefaultSize()
const growAmount = defaultSize + defaultSize / 2

@@ -13,3 +15,3 @@

})
it('should resize automatically at 80% of 10k', () => {
it('should resize automatically at 80% of ' + defaultSize, () => {
const world = createWorld()

@@ -16,0 +18,0 @@ const n = defaultSize * 0.8

import assert, { strictEqual } from 'assert'
import { $componentMap } from '../../src/Component.js'
import { $entityEnabled, $entityMasks, resetGlobals, addEntity, defaultSize } from '../../src/Entity.js'
import { $entityEnabled, $entityMasks, resetGlobals, addEntity, getDefaultSize } from '../../src/Entity.js'
import { $dirtyQueries, $queries, $queryMap } from '../../src/Query.js'
import { createWorld, $size, $bitflag } from '../../src/World.js'
const defaultSize = getDefaultSize()
describe('World Integration Tests', () => {

@@ -8,0 +10,0 @@ afterEach(() => {

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with โšก๏ธ by Socket Inc