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.2.8 to 0.2.9

test.js

792

dist/index.es.js

@@ -1,2 +0,792 @@

const e={bool:"bool",i8:Int8Array,ui8:Uint8Array,ui8c:Uint8ClampedArray,i16:Int16Array,ui16:Uint16Array,i32:Int32Array,ui32:Uint32Array,f32:Float32Array,f64:Float64Array},t=255,r=65535,n=(e,t)=>{const r=new e.constructor(new ArrayBuffer(e.buffer.byteLength+t*e.BYTES_PER_ELEMENT));return r.set(e.buffer),r},o=e=>4*Math.ceil(e/4),i={},a=Symbol("managerRef"),s=Symbol("managerSize"),f=Symbol("maps"),c=Symbol("subarrays"),y=Symbol("managerCursor"),u=Symbol("managerRemoved"),l=Symbol("shadow"),p=Symbol("entityMasks"),g=Symbol("entityEnabled"),d=Symbol("deferredEntityRemovals"),E=Symbol("removedEntities");let b=0;const m=e=>{const t=e[E],r=e[k],n=e[g];if(b>=r-r/5){const t=4*Math.ceil(r/2/4);e[x].forEach((e=>{e.manager._grow(t)})),e[k]+=t}const o=t.length>0?t.pop():b;return n[o]=1,b++,o},h=(e,t)=>e[d].push(t);function w(e){return function(){return e}}function _(e){return function(){return e}}const A=Symbol("queries"),S=Symbol("queryMap"),B=Symbol("queryComponents"),M=(e,t,r)=>{e[S].get(t)||P(e,t),e[S].get(t).enter=r},T=(e,t,r)=>{e[S].get(t)||P(e,t),e[S].get(t).exit=r},P=(e,t)=>{e[S].get(t)||e[S].set(t,{});let r=[],n=[],o=[];t[B].forEach((e=>{"function"==typeof e?("QueryNot"===e.name&&n.push(e()),"QueryChanged"===e.name&&(o.push(e()),r.push(e()))):r.push(e)}));const i=r.reduce(((e,t)=>t[s]>e?t[s]:e),0),a=new Uint32Array(i),f=new Uint8Array(i),c=r.map((t=>e[x].get(t).generationId)),y=(e,t)=>(e[t.generationId]||(e[t.generationId]=0),e[t.generationId]|=t.bitflag,e),u=r.map((t=>e[x].get(t))).reduce(y,{}),l=n.map((t=>e[x].get(t))).reduce(y,{});Object.assign(e[S].get(t),{entities:[],changed:[],enabled:f,components:r,notComponents:n,changedComponents:o,masks:u,notMasks:l,generations:c,indices:a}),e[A].add(t);for(let r=0;r<b;r++)e[g][r]&&v(e,t,r)&&R(e,t,r)},j=e=>{const t=function(e){e[S].has(t)||P(e,t);const r=e[S].get(t);return r.changedComponents.length?((e,t)=>{const r=e[S].get(t);return r.changed.length=0,r.changedComponents.forEach((e=>{const t=e._flatten();for(let e=0;e<t.length;e++){const n=t[e];for(let e=0;e<r.entities.length;e++){const t=r.entities[e];n[t]!==n[l][t]&&(r.changed.push(t),n[l][t]=n[t])}}})),r.changed})(e,t):r.entities};return t[B]=e,t},v=(e,t,r)=>{const{masks:n,notMasks:o,generations:i}=e[S].get(t);for(let t=0;t<i.length;t++){const a=i[t],s=n[a];o[a];if((e[p][a][r]&s)!==s)return!1}return!0},I=(e,t,r)=>r.every((r=>((e,t,r)=>{const{generationId:n,bitflag:o}=e[x].get(r),{masks:i}=e[S].get(t);return(i[n]&o)===o})(e,t,r))),R=(e,t,r)=>{const n=e[S].get(t);n.enabled[r]||(n.enabled[r]=!0,n.entities.push(r),n.indices[r]=n.entities.length-1,n.enter&&n.enter(r))},O=(e,t,r)=>{const n=e[S].get(t);n.enabled[r]&&(n.enabled[r]=!1,n.entities.splice(n.indices[r]),n.exit&&n.exit(r))},x=Symbol("componentMap"),L=Symbol("de$deferredComponentRemovals"),N=(p,g)=>((p,g=1e5)=>{const d=Symbol("manager");i[d]={[s]:g,[f]:{},[c]:{},[a]:d,[y]:0,[u]:[]};const E=p?Object.keys(p):[];let b=E.filter((e=>Array.isArray(p[e])&&"object"==typeof p[e][0]));const m=Object.keys(e).reduce(((e,t)=>({...e,[t]:0})),{});if("string"==typeof p){const t=p,r=g*e[t].BYTES_PER_ELEMENT,n=new ArrayBuffer(r);i[d]=new e[t](n)}else if(Array.isArray(p)){b=p;const{type:n,length:a}=p[0],s=a<t?"ui8":a<r?"ui16":"ui32";if(!a)throw new Error("❌ Must define a length for component array.");if(!e[n])throw new Error(`❌ Invalid component array property type ${n}.`);if(!i[d][c][n]){const t=b,r=t.reduce(((t,r)=>t+e[n].BYTES_PER_ELEMENT),0),f=t.reduce(((e,t)=>e+a),0),y=new ArrayBuffer(o(r*f*g)),u=new e[n](y);u._indexType=s,u._indexBytes=e[s].BYTES_PER_ELEMENT,i[d][c][n]=u}let f=0;for(let e=0;e<g;e++){const t=m[n]+e*a,r=t+a;i[d][e]=i[d][c][n].subarray(t,r),f=r}m[n]=f,i[d]._reset=e=>i[d][e].fill(0),i[d]._set=(e,t)=>i[d][e].set(t,0)}else E.forEach((t=>{if("bool"===p[t]){const r=e.uint8,n=g*e.uint8.BYTES_PER_ELEMENT,o=new ArrayBuffer(n);i[d][f][t]=p[t],i[d][t]=new r(o),i[d][t]._boolType=!0}else if(Array.isArray(p[t])&&"string"==typeof p[t][0]){const r=e.uint8,n=g*e.uint8.BYTES_PER_ELEMENT,o=new ArrayBuffer(n);i[d][f][t]=p[t],i[d][t]=new r(o)}else if(Array.isArray(p[t])&&"object"==typeof p[t][0]){const{type:r,length:n}=p[0];if(!n)throw new Error("❌ Must define a length for component array.");if(!e[r])throw new Error(`❌ Invalid component array property type ${r}.`);if(!i[d][c][r]){const t=b.filter((e=>p[e][0].type===r)),a=t.reduce(((t,n)=>t+e[r].BYTES_PER_ELEMENT),0),s=t.reduce(((e,t)=>e+n),0),f=new ArrayBuffer(o(a*s*g)),y=new e[r](f);y._indexType=index,y._indexBytes=e[index].BYTES_PER_ELEMENT,i[d][c][r]=y}i[d][t]={};let a=0;for(let e=0;e<g;e++){const o=m[r]+e*n,s=o+n;i[d][t][e]=i[d][c][r].subarray(o,s),a=s}m[r]=a,i[d][t]._reset=e=>i[d][t][e].fill(0),i[d][t]._set=(e,r)=>i[d][t][e].set(r,0)}else if("object"==typeof p[t])i[d][t]=Manager(g,p[t],!1);else if("string"==typeof p[t]){const r=p[t],n=g*e[r].BYTES_PER_ELEMENT,o=new ArrayBuffer(n),a=new ArrayBuffer(n);i[d][t]=new e[r](o),i[d][t][l]=new e[r](a)}else{if("function"!=typeof p[t])throw new Error("ECS Error: invalid property type "+p[t]);{const e=p[t],r=g*e.BYTES_PER_ELEMENT,n=new ArrayBuffer(r);i[d][t]=new e(n)}}}));let h;return Object.defineProperty(i[d],"_schema",{value:p}),Object.defineProperty(i[d],"_mapping",{value:e=>i[d][f][e]}),Object.defineProperty(i[d],"_reset",{value:e=>{for(const t of i[d]._props)ArrayBuffer.isView(i[d][t])?ArrayBuffer.isView(i[d][t][e])?i[d][t][e].fill(0):i[d][t][e]=0:i[d][t]._reset(e)}}),Object.defineProperty(i[d],"_set",{value:(e,t)=>{for(const r in t)i[d]._mapping(r)&&"string"==typeof t[r]?i[d].enum(r,e,t[r]):ArrayBuffer.isView(i[d][r])?i[d][r][e]=t[r]:Array.isArray(t[r])&&ArrayBuffer.isView(i[d][r][e])?i[d][r][e].set(t[r],0):"object"==typeof i[d][r]&&i[d][r]._set(e,t[r])}}),Object.defineProperty(i[d],"_get",{value:e=>{const t={};for(const r of i[d]._props)i[d]._mapping(r)?t[r]=i[d].enum(r,e):ArrayBuffer.isView(i[d][r])?t[r]=i[d][r][e]:"object"==typeof i[d][r]&&(ArrayBuffer.isView(i[d][r][e])?t[r]=Array.from(i[d][r][e]):t[r]=i[d][r]._get(e));return t}}),Object.defineProperty(i[d],"_props",{value:E}),Object.defineProperty(i[d],"_flatten",{value:(e=[])=>{if(h)return h;for(const t of i[d]._props)ArrayBuffer.isView(i[d][t])?e.push(i[d][t]):"object"==typeof i[d][t]&&i[d][t]._flatten(e);return h=e,e}}),Object.defineProperty(i[d],"enum",{value:(e,t,r)=>{const n=i[d]._mapping(e);if(n){if(!r)return n[i[d][e][t]];{const o=n.indexOf(r);if(-1===o)return void console.warn(`Value '${r}' is not part of enum.`);i[d][e][t]=o}}else console.warn("Property is not an enum.")}}),Object.defineProperty(i[d],"_grow",{value:e=>{i[d][s]+=e;for(const t of i[d]._props)ArrayBuffer.isView(i[d][t])?(i[d][t]=n(i[d][t],e),i[d][t][l]=n(i[d][t],e)):"object"==typeof i[d][t]&&(ArrayBuffer.isView(i[d][t][eid])||i[d][t]._grow())}}),i[d]})(p,g),C=(e,t)=>{e[x].set(t,{generationId:e[p].length-1,bitflag:e[$],manager:t}),(e=>{e[$]*=2,e[$]>=Math.pow(2,32)&&(e[$]=1,e[p].push(new Uint32Array(e[k])))})(e)},V=(e,t)=>{t.forEach((t=>C(e,t)))},Y=(e,t,r)=>{if(((e,t,r)=>{const{generationId:n,bitflag:o}=e[x].get(t);return(e[p][n][r]&o)===o})(e,t,r))return;const{generationId:n,bitflag:o}=e[x].get(t);e[p][n][r]|=o;e[A].forEach((t=>{const n=t[B];if(!I(e,t,n))return;v(e,t,r)&&R(e,t,r)}))},U=(e,t,r)=>e[L].push(t,r),k=Symbol("size"),$=Symbol("bitflag"),q=(e=1e5)=>{const t={};return t[k]=e,t[g]=new Uint8Array(t[k]),t[p]=[new Uint32Array(e)],t[E]=[],t[$]=1,t[x]=new Map,t[S]=new Map,t[A]=new Set,t[L]=[],t[d]=[],t},z=(e,t)=>r=>{t(e(r)),(e=>{const t=e[L];for(let r=0;r<t.length;r+=2){const n=t[r],o=t[r+1],{generationId:i,bitflag:a}=e[x].get(n);if(!(e[p][i][o]&a))return;e[p][i][o]&=~a,e[A].forEach((t=>{const r=t[B];I(e,t,r)&&v(e,t,o)&&O(e,t,o)}))}t.length=0})(r),(e=>{const t=e[d],r=e[A],n=e[E],o=e[g];for(let i=0;i<t.length;i++){const a=t[i];if(0!==o[a]){r.forEach((e=>{O(e,a)})),n.push(a),o[a]=0;for(let t=0;t<e[p].length;t++)e[p][t][a]=0}}t.length=0})(r)},F=e=>t=>{for(let r=0;r<e.length;r++){(0,e[r])(t)}},Q={bool:"bool",i8:"i8",ui8:"ui8",ui8c:"ui8c",i16:"i16",ui16:"ui16",i32:"i32",ui32:"ui32",f32:"f32",f64:"f64"};export{_ as Changed,w as Not,Q as Types,Y as addComponent,m as addEntity,q as createWorld,N as defineComponent,j as defineQuery,z as defineSystem,M as enterQuery,T as exitQuery,F as pipe,C as registerComponent,V as registerComponents,U as removeComponent,h as removeEntity};
const TYPES_ENUM = {
bool: 'bool',
i8: 'i8',
ui8: 'ui8',
ui8c: 'ui8c',
i16: 'i16',
ui16: 'ui16',
i32: 'i32',
ui32: 'ui32',
f32: 'f32',
f64: 'f64'
};
const TYPES = {
bool: 'bool',
i8: Int8Array,
ui8: Uint8Array,
ui8c: Uint8ClampedArray,
i16: Int16Array,
ui16: Uint16Array,
i32: Int32Array,
ui32: Uint32Array,
f32: Float32Array,
f64: Float64Array
};
const UNSIGNED_MAX = {
uint8: 255,
uint16: 65535,
uint32: 4294967295
};
const grow = (ta, amount) => {
const newTa = new ta.constructor(new ArrayBuffer(ta.buffer.byteLength + amount * ta.BYTES_PER_ELEMENT));
newTa.set(ta.buffer);
return newTa;
};
const roundToMultiple4 = x => Math.ceil(x / 4) * 4;
const managers = {};
const $managerRef = Symbol('managerRef');
const $managerSize = Symbol('managerSize');
const $managerMaps = Symbol('maps');
const $managerSubarrays = Symbol('subarrays');
const $managerCursor = Symbol('managerCursor');
const $managerRemoved = Symbol('managerRemoved');
const $queryShadow = Symbol('queryShadow');
const $serializeShadow = Symbol('$serializeShadow');
const alloc = (schema, size = 1000000) => {
const $manager = Symbol('manager');
managers[$manager] = {
[$managerSize]: size,
[$managerMaps]: {},
[$managerSubarrays]: {},
[$managerRef]: $manager,
[$managerCursor]: 0,
[$managerRemoved]: []
};
const props = schema ? Object.keys(schema) : [];
let arrays = props.filter(p => Array.isArray(schema[p]) && typeof schema[p][0] === 'object');
const cursors = Object.keys(TYPES).reduce((a, type) => ({ ...a,
[type]: 0
}), {});
if (typeof schema === 'string') {
const type = schema;
const totalBytes = size * TYPES[type].BYTES_PER_ELEMENT;
const buffer = new ArrayBuffer(totalBytes);
managers[$manager] = new TYPES[type](buffer);
} else if (Array.isArray(schema)) {
arrays = schema;
const {
type,
length
} = schema[0];
const indexType = length < UNSIGNED_MAX.uint8 ? 'ui8' : length < UNSIGNED_MAX.uint16 ? 'ui16' : 'ui32';
if (!length) throw new Error('❌ Must define a length for component array.');
if (!TYPES[type]) throw new Error(`❌ Invalid component array property type ${type}.`); // create buffer for type if it does not already exist
if (!managers[$manager][$managerSubarrays][type]) {
const relevantArrays = arrays;
const summedBytesPerElement = relevantArrays.reduce((a, p) => a + TYPES[type].BYTES_PER_ELEMENT, 0);
const summedLength = relevantArrays.reduce((a, p) => a + length, 0);
const buffer = new ArrayBuffer(roundToMultiple4(summedBytesPerElement * summedLength * size));
const array = new TYPES[type](buffer);
array._indexType = indexType;
array._indexBytes = TYPES[indexType].BYTES_PER_ELEMENT;
managers[$manager][$managerSubarrays][type] = array;
} // pre-generate subarrays for each eid
let end = 0;
for (let eid = 0; eid < size; eid++) {
const from = cursors[type] + eid * length;
const to = from + length;
managers[$manager][eid] = managers[$manager][$managerSubarrays][type].subarray(from, to);
end = to;
}
cursors[type] = end;
managers[$manager]._reset = eid => managers[$manager][eid].fill(0);
managers[$manager]._set = (eid, values) => managers[$manager][eid].set(values, 0);
} else props.forEach(prop => {
// Boolean Type
if (schema[prop] === 'bool') {
const Type = TYPES.uint8;
const totalBytes = size * TYPES.uint8.BYTES_PER_ELEMENT;
const buffer = new ArrayBuffer(totalBytes);
managers[$manager][$managerMaps][prop] = schema[prop];
managers[$manager][prop] = new Type(buffer);
managers[$manager][prop]._boolType = true; // Enum Type
} else if (Array.isArray(schema[prop]) && typeof schema[prop][0] === 'string') {
const Type = TYPES.uint8;
const totalBytes = size * TYPES.uint8.BYTES_PER_ELEMENT;
const buffer = new ArrayBuffer(totalBytes);
managers[$manager][$managerMaps][prop] = schema[prop];
managers[$manager][prop] = new Type(buffer); // Array Type
} else if (Array.isArray(schema[prop]) && typeof schema[prop][0] === 'object') {
const {
type,
length
} = schema[0];
if (!length) throw new Error('❌ Must define a length for component array.');
if (!TYPES[type]) throw new Error(`❌ Invalid component array property type ${type}.`); // create buffer for type if it does not already exist
if (!managers[$manager][$managerSubarrays][type]) {
const relevantArrays = arrays.filter(p => schema[p][0].type === type);
const summedBytesPerElement = relevantArrays.reduce((a, p) => a + TYPES[type].BYTES_PER_ELEMENT, 0);
const summedLength = relevantArrays.reduce((a, p) => a + length, 0);
const buffer = new ArrayBuffer(roundToMultiple4(summedBytesPerElement * summedLength * size));
const array = new TYPES[type](buffer);
array._indexType = index;
array._indexBytes = TYPES[index].BYTES_PER_ELEMENT;
managers[$manager][$managerSubarrays][type] = array;
} // pre-generate subarrays for each eid
managers[$manager][prop] = {};
let end = 0;
for (let eid = 0; eid < size; eid++) {
const from = cursors[type] + eid * length;
const to = from + length;
managers[$manager][prop][eid] = managers[$manager][$managerSubarrays][type].subarray(from, to);
end = to;
}
cursors[type] = end;
managers[$manager][prop]._reset = eid => managers[$manager][prop][eid].fill(0);
managers[$manager][prop]._set = (eid, values) => managers[$manager][prop][eid].set(values, 0); // Object Type
} else if (typeof schema[prop] === 'object') {
managers[$manager][prop] = Manager(size, schema[prop], false); // String Type
} else if (typeof schema[prop] === 'string') {
const type = schema[prop];
const totalBytes = size * TYPES[type].BYTES_PER_ELEMENT;
const buffer = new ArrayBuffer(totalBytes);
const queryShadowBuffer = new ArrayBuffer(totalBytes);
const serializeShadowBuffer = new ArrayBuffer(totalBytes);
managers[$manager][prop] = new TYPES[type](buffer);
managers[$manager][prop][$queryShadow] = new TYPES[type](queryShadowBuffer);
managers[$manager][prop][$serializeShadow] = new TYPES[type](serializeShadowBuffer); // TypedArray Type
} else if (typeof schema[prop] === 'function') {
const Type = schema[prop];
const totalBytes = size * Type.BYTES_PER_ELEMENT;
const buffer = new ArrayBuffer(totalBytes);
managers[$manager][prop] = new Type(buffer);
} else {
throw new Error(`ECS Error: invalid property type ${schema[prop]}`);
}
}); // methods
Object.defineProperty(managers[$manager], '_schema', {
value: schema
});
Object.defineProperty(managers[$manager], '_mapping', {
value: prop => managers[$manager][$managerMaps][prop]
}); // Recursively set all values to 0
Object.defineProperty(managers[$manager], '_reset', {
value: eid => {
for (const prop of managers[$manager]._props) {
if (ArrayBuffer.isView(managers[$manager][prop])) {
if (ArrayBuffer.isView(managers[$manager][prop][eid])) {
managers[$manager][prop][eid].fill(0);
} else {
managers[$manager][prop][eid] = 0;
}
} else {
managers[$manager][prop]._reset(eid);
}
}
}
}); // Recursively set all values from a supplied object
Object.defineProperty(managers[$manager], '_set', {
value: (eid, values) => {
for (const prop in values) {
const mapping = managers[$manager]._mapping(prop);
if (mapping && typeof values[prop] === 'string') {
managers[$manager].enum(prop, eid, values[prop]);
} else if (ArrayBuffer.isView(managers[$manager][prop])) {
managers[$manager][prop][eid] = values[prop];
} else if (Array.isArray(values[prop]) && ArrayBuffer.isView(managers[$manager][prop][eid])) {
managers[$manager][prop][eid].set(values[prop], 0);
} else if (typeof managers[$manager][prop] === 'object') {
managers[$manager][prop]._set(eid, values[prop]);
}
}
}
});
Object.defineProperty(managers[$manager], '_get', {
value: eid => {
const obj = {};
for (const prop of managers[$manager]._props) {
const mapping = managers[$manager]._mapping(prop);
if (mapping) {
obj[prop] = managers[$manager].enum(prop, eid);
} else if (ArrayBuffer.isView(managers[$manager][prop])) {
obj[prop] = managers[$manager][prop][eid];
} else if (typeof managers[$manager][prop] === 'object') {
if (ArrayBuffer.isView(managers[$manager][prop][eid])) {
obj[prop] = Array.from(managers[$manager][prop][eid]);
} else {
obj[prop] = managers[$manager][prop]._get(eid);
}
}
}
return obj;
}
});
Object.defineProperty(managers[$manager], '_props', {
value: props
}); // Aggregate all typedArrays into single kvp array (memoized)
let flattened;
Object.defineProperty(managers[$manager], '_flatten', {
value: (flat = []) => {
if (flattened) return flattened;
for (const prop of managers[$manager]._props) {
if (ArrayBuffer.isView(managers[$manager][prop])) {
flat.push(managers[$manager][prop]);
} else if (typeof managers[$manager][prop] === 'object') {
managers[$manager][prop]._flatten(flat);
}
}
flattened = flat;
return flat;
}
});
Object.defineProperty(managers[$manager], 'enum', {
value: (prop, eid, value) => {
const mapping = managers[$manager]._mapping(prop);
if (!mapping) {
console.warn('Property is not an enum.');
return undefined;
}
if (value) {
const index = mapping.indexOf(value);
if (index === -1) {
console.warn(`Value '${value}' is not part of enum.`);
return undefined;
}
managers[$manager][prop][eid] = index;
} else {
return mapping[managers[$manager][prop][eid]];
}
}
});
Object.defineProperty(managers[$manager], '_grow', {
value: amount => {
managers[$manager][$managerSize] += amount;
for (const prop of managers[$manager]._props) {
if (ArrayBuffer.isView(managers[$manager][prop])) {
managers[$manager][prop] = grow(managers[$manager][prop], amount);
managers[$manager][prop][$queryShadow] = grow(managers[$manager][prop], amount);
} else if (typeof managers[$manager][prop] === 'object') {
if (ArrayBuffer.isView(managers[$manager][prop][eid])) ; else {
managers[$manager][prop]._grow();
}
}
}
}
});
return managers[$manager];
};
const $entityMasks = Symbol('entityMasks');
const $entityEnabled = Symbol('entityEnabled');
const $deferredEntityRemovals = Symbol('deferredEntityRemovals');
const $removedEntities = Symbol('removedEntities'); // 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
let globalEntityCursor = 0;
const getEntityCursor = () => globalEntityCursor;
const addEntity = world => {
const removed = world[$removedEntities];
const size = world[$size];
const enabled = world[$entityEnabled];
if (globalEntityCursor >= size - size / 5) {
// if 80% full
const amount = Math.ceil(size / 2 / 4) * 4; // grow by half the original size rounded up to a multiple of 4
// grow data stores
world[$componentMap].forEach(component => {
component.manager._grow(amount);
});
world[$size] += amount; // TODO: grow metadata on world mappings for queries/components enabled/etc
}
const eid = removed.length > 0 ? removed.pop() : globalEntityCursor;
enabled[eid] = 1;
globalEntityCursor++;
return eid;
};
const removeEntity = (world, eid) => world[$deferredEntityRemovals].push(eid);
const commitEntityRemovals = world => {
const deferred = world[$deferredEntityRemovals];
const queries = world[$queries];
const removed = world[$removedEntities];
const enabled = world[$entityEnabled];
for (let i = 0; i < deferred.length; i++) {
const eid = deferred[i]; // Check if entity is already removed
if (enabled[eid] === 0) continue; // Remove entity from all queries
// TODO: archetype graph
queries.forEach(query => {
queryRemoveEntity(query, eid);
}); // Free the entity
removed.push(eid);
enabled[eid] = 0; // Clear component bitmasks
for (let i = 0; i < world[$entityMasks].length; i++) world[$entityMasks][i][eid] = 0;
}
deferred.length = 0;
};
const diff = (world, query) => {
const q = world[$queryMap].get(query);
q.changed.length = 0;
const flat = q.flatProps;
for (let i = 0; i < q.entities.length; i++) {
const eid = q.entities[i];
let dirty = false;
for (let pid = 0; pid < flat.length; pid++) {
const prop = flat[pid];
if (ArrayBuffer.isView(prop[eid])) {
for (let i = 0; i < prop[eid].length; i++) {
if (prop[eid][i] !== prop[eid][$queryShadow][i]) {
dirty = true;
prop[eid][$queryShadow][i] = prop[eid][i];
}
}
} else {
if (prop[eid] !== prop[$queryShadow][eid]) {
dirty = true;
prop[$queryShadow][eid] = prop[eid];
}
}
}
if (dirty) q.changed.push(eid);
}
return q.changed;
};
const canonicalize = target => {
let componentProps;
let changedProps = new Set();
if (Array.isArray(target)) {
componentProps = target.map(p => {
if (p._flatten) {
return p._flatten();
} else if (typeof p === 'function' && p.name === 'QueryChanged') {
p = p();
if (p._flatten) {
let props = p._flatten();
props.forEach(x => changedProps.add(x));
return props;
}
changedProps.add(p);
return [p];
}
}).reduce((a, v) => a.concat(v), []);
} else {
target[$componentMap].forEach(c => {
componentProps = componentProps.concat(c._flatten());
});
}
return [componentProps, changedProps];
};
const defineSerializer = (target, maxBytes = 5000000) => {
const buffer = new ArrayBuffer(maxBytes);
const view = new DataView(buffer);
const [componentProps, changedProps] = canonicalize(target);
return ents => {
if (!ents.length) return;
let where = 0; // iterate over component props
for (let pid = 0; pid < componentProps.length; pid++) {
const prop = componentProps[pid];
const diff = changedProps.has(prop); // write pid
view.setUint8(where, pid);
where += 1; // save space for entity count
const countWhere = where;
where += 4;
let count = 0; // write eid,val
for (let i = 0; i < ents.length; i++) {
const eid = ents[i]; // skip if diffing and no change
if (diff && prop[eid] === prop[$serializeShadow][eid]) {
continue;
}
prop[$serializeShadow][eid] = prop[eid];
count++; // write eid
view.setUint32(where, eid);
where += 4; // if property is an array
if (ArrayBuffer.isView(prop[eid])) {
const type = prop[eid].constructor.name.replace('Array', '');
const indexType = prop[eid]._indexType;
const indexBytes = prop[eid]._indexBytes; // add space for count of dirty array elements
const countWhere2 = where;
where += 1;
let count2 = 0; // write array values
for (let i = 0; i < prop[eid].length; i++) {
const val = prop[eid][i]; // write array index
view[`set${indexType}`](where, i);
where += indexBytes; // write value at that index
view[`set${type}`](where, val);
where += prop[eid].BYTES_PER_ELEMENT;
count2++;
}
view[`set${indexType}`](countWhere2, count2);
} else {
// regular property values
const type = prop.constructor.name.replace('Array', ''); // set value next [type] bytes
view[`set${type}`](where, prop[eid]);
where += prop.BYTES_PER_ELEMENT;
}
}
view.setUint32(countWhere, count);
}
return buffer.slice(0, where);
};
};
const defineDeserializer = target => {
const [componentProps] = canonicalize(target);
return packet => {
const view = new DataView(packet);
let where = 0; // pid
const pid = view.getUint8(where);
where += 1; // entity count
const entityCount = view.getUint32(where);
where += 4; // typed array
const ta = componentProps[pid]; // Get the properties and set the new state
for (let i = 0; i < entityCount; i++) {
const eid = view.getUint32(where);
where += 4;
if (ArrayBuffer.isView(ta[eid])) {
const array = ta[eid];
const count = view[`get${array._indexType}`];
where += array._indexBytes; // iterate over count
for (let i = 0; i < count; i++) {
const value = view[`get${array.constructor.name.replace('Array', '')}`](where);
where += array.BYTES_PER_ELEMENT;
ta[eid][i] = value;
}
} else {
let value = view[`get${ta.constructor.name.replace('Array', '')}`](where);
where += ta.BYTES_PER_ELEMENT;
ta[eid] = value;
}
}
};
};
function Not(c) {
return function QueryNot() {
return c;
};
}
function Changed(c) {
return function QueryChanged() {
return c;
};
}
const $queries = Symbol('queries');
const $queryMap = Symbol('queryMap');
const $queryComponents = Symbol('queryComponents');
const enterQuery = (world, query, fn) => {
if (!world[$queryMap].get(query)) registerQuery(world, query);
world[$queryMap].get(query).enter = fn;
};
const exitQuery = (world, query, fn) => {
if (!world[$queryMap].get(query)) registerQuery(world, query);
world[$queryMap].get(query).exit = fn;
};
const registerQuery = (world, query) => {
if (!world[$queryMap].get(query)) world[$queryMap].set(query, {});
let components = [];
let notComponents = [];
let changedComponents = [];
query[$queryComponents].forEach(c => {
if (typeof c === 'function') {
if (c.name === 'QueryNot') {
notComponents.push(c());
}
if (c.name === 'QueryChanged') {
changedComponents.push(c());
components.push(c());
}
} else {
components.push(c);
}
});
const size = components.reduce((a, c) => c[$managerSize] > a ? c[$managerSize] : a, 0);
const entities = [];
const changed = [];
const indices = new Uint32Array(size);
const enabled = new Uint8Array(size);
const generations = components.map(c => world[$componentMap].get(c).generationId);
const mapComponents = c => world[$componentMap].get(c);
const reduceBitmasks = (a, c) => {
if (!a[c.generationId]) a[c.generationId] = 0;
a[c.generationId] |= c.bitflag;
return a;
};
const masks = components.map(mapComponents).reduce(reduceBitmasks, {});
const notMasks = notComponents.map(c => world[$componentMap].get(c)).reduce(reduceBitmasks, {});
const flatProps = components.map(c => !ArrayBuffer.isView(c) ? c._flatten() : [c]).reduce((a, v) => a.concat(v), []);
Object.assign(world[$queryMap].get(query), {
entities,
changed,
enabled,
components,
notComponents,
changedComponents,
masks,
notMasks,
generations,
indices,
flatProps
});
world[$queries].add(query);
for (let eid = 0; eid < getEntityCursor(); eid++) {
if (!world[$entityEnabled][eid]) continue;
if (queryCheckEntity(world, query, eid)) {
queryAddEntity(world, query, eid);
}
}
};
const defineQuery = components => {
const query = function (world) {
if (!world[$queryMap].has(query)) registerQuery(world, query);
const q = world[$queryMap].get(query);
if (q.changedComponents.length) return diff(world, query);
return q.entities;
};
query[$queryComponents] = components;
return query;
}; // TODO: archetype graph
const queryCheckEntity = (world, query, eid) => {
const {
masks,
notMasks,
generations
} = world[$queryMap].get(query);
for (let i = 0; i < generations.length; i++) {
const generationId = generations[i];
const qMask = masks[generationId];
const qNotMask = notMasks[generationId];
const eMask = world[$entityMasks][generationId][eid];
if ((eMask & qMask) !== qMask) {
return false;
} // if ((eMask | ~qNotMask) === ~qNotMask) {
// return false
// }
}
return true;
};
const queryCheckComponent = (world, query, component) => {
const {
generationId,
bitflag
} = world[$componentMap].get(component);
const {
masks
} = world[$queryMap].get(query);
const mask = masks[generationId];
return (mask & bitflag) === bitflag;
};
const queryCheckComponents = (world, query, components) => {
return components.every(c => queryCheckComponent(world, query, c));
};
const queryAddEntity = (world, query, eid) => {
const q = world[$queryMap].get(query);
if (q.enabled[eid]) return;
q.enabled[eid] = true;
q.entities.push(eid);
q.indices[eid] = q.entities.length - 1;
if (q.enter) q.enter(eid);
};
const queryRemoveEntity = (world, query, eid) => {
const q = world[$queryMap].get(query);
if (!q.enabled[eid]) return;
q.enabled[eid] = false;
q.entities.splice(q.indices[eid]);
if (q.exit) q.exit(eid);
};
const $componentMap = Symbol('componentMap');
const $deferredComponentRemovals = Symbol('de$deferredComponentRemovals');
const defineComponent = (schema, n) => alloc(schema, n);
const incrementBitflag = world => {
world[$bitflag] *= 2;
if (world[$bitflag] >= Math.pow(2, 32)) {
world[$bitflag] = 1;
world[$entityMasks].push(new Uint32Array(world[$size]));
}
};
const registerComponent = (world, component) => {
world[$componentMap].set(component, {
generationId: world[$entityMasks].length - 1,
bitflag: world[$bitflag],
manager: component
});
incrementBitflag(world);
};
const registerComponents = (world, components) => {
components.forEach(c => registerComponent(world, c));
};
const hasComponent = (world, component, eid) => {
const {
generationId,
bitflag
} = world[$componentMap].get(component);
const mask = world[$entityMasks][generationId][eid];
return (mask & bitflag) === bitflag;
};
const addComponent = (world, component, eid) => {
if (hasComponent(world, component, eid)) return; // Add bitflag to entity bitmask
const {
generationId,
bitflag
} = world[$componentMap].get(component);
world[$entityMasks][generationId][eid] |= bitflag; // Zero out each property value
// component._reset(eid)
// todo: archetype graph
const queries = world[$queries];
queries.forEach(query => {
const components = query[$queryComponents];
if (!queryCheckComponents(world, query, components)) return;
const match = queryCheckEntity(world, query, eid);
if (match) queryAddEntity(world, query, eid);
});
};
const removeComponent = (world, component, eid) => world[$deferredComponentRemovals].push(component, eid);
const commitComponentRemovals = world => {
const deferredComponentRemovals = world[$deferredComponentRemovals];
for (let i = 0; i < deferredComponentRemovals.length; i += 2) {
const component = deferredComponentRemovals[i];
const eid = deferredComponentRemovals[i + 1];
const {
generationId,
bitflag
} = world[$componentMap].get(component);
if (!(world[$entityMasks][generationId][eid] & bitflag)) return; // Remove flag from entity bitmask
world[$entityMasks][generationId][eid] &= ~bitflag; // todo: archetype graph
const queries = world[$queries];
queries.forEach(query => {
const components = query[$queryComponents];
if (!queryCheckComponents(world, query, components)) return;
const match = queryCheckEntity(world, query, eid);
if (match) queryRemoveEntity(world, query, eid);
});
}
deferredComponentRemovals.length = 0;
};
const $size = Symbol('size');
const $bitflag = Symbol('bitflag');
const createWorld = (size = 1000000) => {
const world = {};
world[$size] = size;
world[$entityEnabled] = new Uint8Array(world[$size]);
world[$entityMasks] = [new Uint32Array(size)];
world[$removedEntities] = [];
world[$bitflag] = 1;
world[$componentMap] = new Map();
world[$queryMap] = new Map();
world[$queries] = new Set();
world[$deferredComponentRemovals] = [];
world[$deferredEntityRemovals] = [];
return world;
};
const defineSystem = update => {
const system = world => {
update(world);
commitComponentRemovals(world);
commitEntityRemovals(world);
};
Object.defineProperty(system, 'name', {
value: (update.name || "AnonymousSystem") + "_internal",
configurable: true
});
return system;
};
const pipe = fns => world => {
for (let i = 0; i < fns.length; i++) {
const fn = fns[i];
fn(world);
}
};
const Types = TYPES_ENUM;
export { Changed, Not, Types, addComponent, addEntity, createWorld, defineComponent, defineDeserializer, defineQuery, defineSerializer, defineSystem, enterQuery, exitQuery, pipe, registerComponent, registerComponents, removeComponent, removeEntity };
//# sourceMappingURL=index.es.js.map

@@ -1,2 +0,813 @@

"use strict";Object.defineProperty(exports,"__esModule",{value:!0});const e={bool:"bool",i8:Int8Array,ui8:Uint8Array,ui8c:Uint8ClampedArray,i16:Int16Array,ui16:Uint16Array,i32:Int32Array,ui32:Uint32Array,f32:Float32Array,f64:Float64Array},t=255,r=65535,n=(e,t)=>{const r=new e.constructor(new ArrayBuffer(e.buffer.byteLength+t*e.BYTES_PER_ELEMENT));return r.set(e.buffer),r},o=e=>4*Math.ceil(e/4),i={},s=Symbol("managerRef"),a=Symbol("managerSize"),f=Symbol("maps"),y=Symbol("subarrays"),c=Symbol("managerCursor"),u=Symbol("managerRemoved"),p=Symbol("shadow"),l=Symbol("entityMasks"),g=Symbol("entityEnabled"),d=Symbol("deferredEntityRemovals"),m=Symbol("removedEntities");let E=0;const b=Symbol("queries"),h=Symbol("queryMap"),_=Symbol("queryComponents"),w=(e,t)=>{e[h].get(t)||e[h].set(t,{});let r=[],n=[],o=[];t[_].forEach((e=>{"function"==typeof e?("QueryNot"===e.name&&n.push(e()),"QueryChanged"===e.name&&(o.push(e()),r.push(e()))):r.push(e)}));const i=r.reduce(((e,t)=>t[a]>e?t[a]:e),0),s=new Uint32Array(i),f=new Uint8Array(i),y=r.map((t=>e[M].get(t).generationId)),c=(e,t)=>(e[t.generationId]||(e[t.generationId]=0),e[t.generationId]|=t.bitflag,e),u=r.map((t=>e[M].get(t))).reduce(c,{}),p=n.map((t=>e[M].get(t))).reduce(c,{});Object.assign(e[h].get(t),{entities:[],changed:[],enabled:f,components:r,notComponents:n,changedComponents:o,masks:u,notMasks:p,generations:y,indices:s}),e[b].add(t);for(let r=0;r<E;r++)e[g][r]&&A(e,t,r)&&B(e,t,r)},A=(e,t,r)=>{const{masks:n,notMasks:o,generations:i}=e[h].get(t);for(let t=0;t<i.length;t++){const s=i[t],a=n[s];o[s];if((e[l][s][r]&a)!==a)return!1}return!0},S=(e,t,r)=>r.every((r=>((e,t,r)=>{const{generationId:n,bitflag:o}=e[M].get(r),{masks:i}=e[h].get(t);return(i[n]&o)===o})(e,t,r))),B=(e,t,r)=>{const n=e[h].get(t);n.enabled[r]||(n.enabled[r]=!0,n.entities.push(r),n.indices[r]=n.entities.length-1,n.enter&&n.enter(r))},x=(e,t,r)=>{const n=e[h].get(t);n.enabled[r]&&(n.enabled[r]=!1,n.entities.splice(n.indices[r]),n.exit&&n.exit(r))},M=Symbol("componentMap"),T=Symbol("de$deferredComponentRemovals"),v=(e,t)=>{e[M].set(t,{generationId:e[l].length-1,bitflag:e[j],manager:t}),(e=>{e[j]*=2,e[j]>=Math.pow(2,32)&&(e[j]=1,e[l].push(new Uint32Array(e[P])))})(e)},P=Symbol("size"),j=Symbol("bitflag"),C={bool:"bool",i8:"i8",ui8:"ui8",ui8c:"ui8c",i16:"i16",ui16:"ui16",i32:"i32",ui32:"ui32",f32:"f32",f64:"f64"};exports.Changed=function(e){return function(){return e}},exports.Not=function(e){return function(){return e}},exports.Types=C,exports.addComponent=(e,t,r)=>{if(((e,t,r)=>{const{generationId:n,bitflag:o}=e[M].get(t);return(e[l][n][r]&o)===o})(e,t,r))return;const{generationId:n,bitflag:o}=e[M].get(t);e[l][n][r]|=o;e[b].forEach((t=>{const n=t[_];if(!S(e,t,n))return;A(e,t,r)&&B(e,t,r)}))},exports.addEntity=e=>{const t=e[m],r=e[P],n=e[g];if(E>=r-r/5){const t=4*Math.ceil(r/2/4);e[M].forEach((e=>{e.manager._grow(t)})),e[P]+=t}const o=t.length>0?t.pop():E;return n[o]=1,E++,o},exports.createWorld=(e=1e5)=>{const t={};return t[P]=e,t[g]=new Uint8Array(t[P]),t[l]=[new Uint32Array(e)],t[m]=[],t[j]=1,t[M]=new Map,t[h]=new Map,t[b]=new Set,t[T]=[],t[d]=[],t},exports.defineComponent=(l,g)=>((l,g=1e5)=>{const d=Symbol("manager");i[d]={[a]:g,[f]:{},[y]:{},[s]:d,[c]:0,[u]:[]};const m=l?Object.keys(l):[];let E=m.filter((e=>Array.isArray(l[e])&&"object"==typeof l[e][0]));const b=Object.keys(e).reduce(((e,t)=>({...e,[t]:0})),{});if("string"==typeof l){const t=l,r=g*e[t].BYTES_PER_ELEMENT,n=new ArrayBuffer(r);i[d]=new e[t](n)}else if(Array.isArray(l)){E=l;const{type:n,length:s}=l[0],a=s<t?"ui8":s<r?"ui16":"ui32";if(!s)throw new Error("❌ Must define a length for component array.");if(!e[n])throw new Error(`❌ Invalid component array property type ${n}.`);if(!i[d][y][n]){const t=E,r=t.reduce(((t,r)=>t+e[n].BYTES_PER_ELEMENT),0),f=t.reduce(((e,t)=>e+s),0),c=new ArrayBuffer(o(r*f*g)),u=new e[n](c);u._indexType=a,u._indexBytes=e[a].BYTES_PER_ELEMENT,i[d][y][n]=u}let f=0;for(let e=0;e<g;e++){const t=b[n]+e*s,r=t+s;i[d][e]=i[d][y][n].subarray(t,r),f=r}b[n]=f,i[d]._reset=e=>i[d][e].fill(0),i[d]._set=(e,t)=>i[d][e].set(t,0)}else m.forEach((t=>{if("bool"===l[t]){const r=e.uint8,n=g*e.uint8.BYTES_PER_ELEMENT,o=new ArrayBuffer(n);i[d][f][t]=l[t],i[d][t]=new r(o),i[d][t]._boolType=!0}else if(Array.isArray(l[t])&&"string"==typeof l[t][0]){const r=e.uint8,n=g*e.uint8.BYTES_PER_ELEMENT,o=new ArrayBuffer(n);i[d][f][t]=l[t],i[d][t]=new r(o)}else if(Array.isArray(l[t])&&"object"==typeof l[t][0]){const{type:r,length:n}=l[0];if(!n)throw new Error("❌ Must define a length for component array.");if(!e[r])throw new Error(`❌ Invalid component array property type ${r}.`);if(!i[d][y][r]){const t=E.filter((e=>l[e][0].type===r)),s=t.reduce(((t,n)=>t+e[r].BYTES_PER_ELEMENT),0),a=t.reduce(((e,t)=>e+n),0),f=new ArrayBuffer(o(s*a*g)),c=new e[r](f);c._indexType=index,c._indexBytes=e[index].BYTES_PER_ELEMENT,i[d][y][r]=c}i[d][t]={};let s=0;for(let e=0;e<g;e++){const o=b[r]+e*n,a=o+n;i[d][t][e]=i[d][y][r].subarray(o,a),s=a}b[r]=s,i[d][t]._reset=e=>i[d][t][e].fill(0),i[d][t]._set=(e,r)=>i[d][t][e].set(r,0)}else if("object"==typeof l[t])i[d][t]=Manager(g,l[t],!1);else if("string"==typeof l[t]){const r=l[t],n=g*e[r].BYTES_PER_ELEMENT,o=new ArrayBuffer(n),s=new ArrayBuffer(n);i[d][t]=new e[r](o),i[d][t][p]=new e[r](s)}else{if("function"!=typeof l[t])throw new Error("ECS Error: invalid property type "+l[t]);{const e=l[t],r=g*e.BYTES_PER_ELEMENT,n=new ArrayBuffer(r);i[d][t]=new e(n)}}}));let h;return Object.defineProperty(i[d],"_schema",{value:l}),Object.defineProperty(i[d],"_mapping",{value:e=>i[d][f][e]}),Object.defineProperty(i[d],"_reset",{value:e=>{for(const t of i[d]._props)ArrayBuffer.isView(i[d][t])?ArrayBuffer.isView(i[d][t][e])?i[d][t][e].fill(0):i[d][t][e]=0:i[d][t]._reset(e)}}),Object.defineProperty(i[d],"_set",{value:(e,t)=>{for(const r in t)i[d]._mapping(r)&&"string"==typeof t[r]?i[d].enum(r,e,t[r]):ArrayBuffer.isView(i[d][r])?i[d][r][e]=t[r]:Array.isArray(t[r])&&ArrayBuffer.isView(i[d][r][e])?i[d][r][e].set(t[r],0):"object"==typeof i[d][r]&&i[d][r]._set(e,t[r])}}),Object.defineProperty(i[d],"_get",{value:e=>{const t={};for(const r of i[d]._props)i[d]._mapping(r)?t[r]=i[d].enum(r,e):ArrayBuffer.isView(i[d][r])?t[r]=i[d][r][e]:"object"==typeof i[d][r]&&(ArrayBuffer.isView(i[d][r][e])?t[r]=Array.from(i[d][r][e]):t[r]=i[d][r]._get(e));return t}}),Object.defineProperty(i[d],"_props",{value:m}),Object.defineProperty(i[d],"_flatten",{value:(e=[])=>{if(h)return h;for(const t of i[d]._props)ArrayBuffer.isView(i[d][t])?e.push(i[d][t]):"object"==typeof i[d][t]&&i[d][t]._flatten(e);return h=e,e}}),Object.defineProperty(i[d],"enum",{value:(e,t,r)=>{const n=i[d]._mapping(e);if(n){if(!r)return n[i[d][e][t]];{const o=n.indexOf(r);if(-1===o)return void console.warn(`Value '${r}' is not part of enum.`);i[d][e][t]=o}}else console.warn("Property is not an enum.")}}),Object.defineProperty(i[d],"_grow",{value:e=>{i[d][a]+=e;for(const t of i[d]._props)ArrayBuffer.isView(i[d][t])?(i[d][t]=n(i[d][t],e),i[d][t][p]=n(i[d][t],e)):"object"==typeof i[d][t]&&(ArrayBuffer.isView(i[d][t][eid])||i[d][t]._grow())}}),i[d]})(l,g),exports.defineQuery=e=>{const t=function(e){e[h].has(t)||w(e,t);const r=e[h].get(t);return r.changedComponents.length?((e,t)=>{const r=e[h].get(t);return r.changed.length=0,r.changedComponents.forEach((e=>{const t=e._flatten();for(let e=0;e<t.length;e++){const n=t[e];for(let e=0;e<r.entities.length;e++){const t=r.entities[e];n[t]!==n[p][t]&&(r.changed.push(t),n[p][t]=n[t])}}})),r.changed})(e,t):r.entities};return t[_]=e,t},exports.defineSystem=(e,t)=>r=>{t(e(r)),(e=>{const t=e[T];for(let r=0;r<t.length;r+=2){const n=t[r],o=t[r+1],{generationId:i,bitflag:s}=e[M].get(n);if(!(e[l][i][o]&s))return;e[l][i][o]&=~s,e[b].forEach((t=>{const r=t[_];S(e,t,r)&&A(e,t,o)&&x(e,t,o)}))}t.length=0})(r),(e=>{const t=e[d],r=e[b],n=e[m],o=e[g];for(let i=0;i<t.length;i++){const s=t[i];if(0!==o[s]){r.forEach((e=>{x(e,s)})),n.push(s),o[s]=0;for(let t=0;t<e[l].length;t++)e[l][t][s]=0}}t.length=0})(r)},exports.enterQuery=(e,t,r)=>{e[h].get(t)||w(e,t),e[h].get(t).enter=r},exports.exitQuery=(e,t,r)=>{e[h].get(t)||w(e,t),e[h].get(t).exit=r},exports.pipe=e=>t=>{for(let r=0;r<e.length;r++){(0,e[r])(t)}},exports.registerComponent=v,exports.registerComponents=(e,t)=>{t.forEach((t=>v(e,t)))},exports.removeComponent=(e,t,r)=>e[T].push(t,r),exports.removeEntity=(e,t)=>e[d].push(t);
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
const TYPES_ENUM = {
bool: 'bool',
i8: 'i8',
ui8: 'ui8',
ui8c: 'ui8c',
i16: 'i16',
ui16: 'ui16',
i32: 'i32',
ui32: 'ui32',
f32: 'f32',
f64: 'f64'
};
const TYPES = {
bool: 'bool',
i8: Int8Array,
ui8: Uint8Array,
ui8c: Uint8ClampedArray,
i16: Int16Array,
ui16: Uint16Array,
i32: Int32Array,
ui32: Uint32Array,
f32: Float32Array,
f64: Float64Array
};
const UNSIGNED_MAX = {
uint8: 255,
uint16: 65535,
uint32: 4294967295
};
const grow = (ta, amount) => {
const newTa = new ta.constructor(new ArrayBuffer(ta.buffer.byteLength + amount * ta.BYTES_PER_ELEMENT));
newTa.set(ta.buffer);
return newTa;
};
const roundToMultiple4 = x => Math.ceil(x / 4) * 4;
const managers = {};
const $managerRef = Symbol('managerRef');
const $managerSize = Symbol('managerSize');
const $managerMaps = Symbol('maps');
const $managerSubarrays = Symbol('subarrays');
const $managerCursor = Symbol('managerCursor');
const $managerRemoved = Symbol('managerRemoved');
const $queryShadow = Symbol('queryShadow');
const $serializeShadow = Symbol('$serializeShadow');
const alloc = (schema, size = 1000000) => {
const $manager = Symbol('manager');
managers[$manager] = {
[$managerSize]: size,
[$managerMaps]: {},
[$managerSubarrays]: {},
[$managerRef]: $manager,
[$managerCursor]: 0,
[$managerRemoved]: []
};
const props = schema ? Object.keys(schema) : [];
let arrays = props.filter(p => Array.isArray(schema[p]) && typeof schema[p][0] === 'object');
const cursors = Object.keys(TYPES).reduce((a, type) => ({ ...a,
[type]: 0
}), {});
if (typeof schema === 'string') {
const type = schema;
const totalBytes = size * TYPES[type].BYTES_PER_ELEMENT;
const buffer = new ArrayBuffer(totalBytes);
managers[$manager] = new TYPES[type](buffer);
} else if (Array.isArray(schema)) {
arrays = schema;
const {
type,
length
} = schema[0];
const indexType = length < UNSIGNED_MAX.uint8 ? 'ui8' : length < UNSIGNED_MAX.uint16 ? 'ui16' : 'ui32';
if (!length) throw new Error('❌ Must define a length for component array.');
if (!TYPES[type]) throw new Error(`❌ Invalid component array property type ${type}.`); // create buffer for type if it does not already exist
if (!managers[$manager][$managerSubarrays][type]) {
const relevantArrays = arrays;
const summedBytesPerElement = relevantArrays.reduce((a, p) => a + TYPES[type].BYTES_PER_ELEMENT, 0);
const summedLength = relevantArrays.reduce((a, p) => a + length, 0);
const buffer = new ArrayBuffer(roundToMultiple4(summedBytesPerElement * summedLength * size));
const array = new TYPES[type](buffer);
array._indexType = indexType;
array._indexBytes = TYPES[indexType].BYTES_PER_ELEMENT;
managers[$manager][$managerSubarrays][type] = array;
} // pre-generate subarrays for each eid
let end = 0;
for (let eid = 0; eid < size; eid++) {
const from = cursors[type] + eid * length;
const to = from + length;
managers[$manager][eid] = managers[$manager][$managerSubarrays][type].subarray(from, to);
end = to;
}
cursors[type] = end;
managers[$manager]._reset = eid => managers[$manager][eid].fill(0);
managers[$manager]._set = (eid, values) => managers[$manager][eid].set(values, 0);
} else props.forEach(prop => {
// Boolean Type
if (schema[prop] === 'bool') {
const Type = TYPES.uint8;
const totalBytes = size * TYPES.uint8.BYTES_PER_ELEMENT;
const buffer = new ArrayBuffer(totalBytes);
managers[$manager][$managerMaps][prop] = schema[prop];
managers[$manager][prop] = new Type(buffer);
managers[$manager][prop]._boolType = true; // Enum Type
} else if (Array.isArray(schema[prop]) && typeof schema[prop][0] === 'string') {
const Type = TYPES.uint8;
const totalBytes = size * TYPES.uint8.BYTES_PER_ELEMENT;
const buffer = new ArrayBuffer(totalBytes);
managers[$manager][$managerMaps][prop] = schema[prop];
managers[$manager][prop] = new Type(buffer); // Array Type
} else if (Array.isArray(schema[prop]) && typeof schema[prop][0] === 'object') {
const {
type,
length
} = schema[0];
if (!length) throw new Error('❌ Must define a length for component array.');
if (!TYPES[type]) throw new Error(`❌ Invalid component array property type ${type}.`); // create buffer for type if it does not already exist
if (!managers[$manager][$managerSubarrays][type]) {
const relevantArrays = arrays.filter(p => schema[p][0].type === type);
const summedBytesPerElement = relevantArrays.reduce((a, p) => a + TYPES[type].BYTES_PER_ELEMENT, 0);
const summedLength = relevantArrays.reduce((a, p) => a + length, 0);
const buffer = new ArrayBuffer(roundToMultiple4(summedBytesPerElement * summedLength * size));
const array = new TYPES[type](buffer);
array._indexType = index;
array._indexBytes = TYPES[index].BYTES_PER_ELEMENT;
managers[$manager][$managerSubarrays][type] = array;
} // pre-generate subarrays for each eid
managers[$manager][prop] = {};
let end = 0;
for (let eid = 0; eid < size; eid++) {
const from = cursors[type] + eid * length;
const to = from + length;
managers[$manager][prop][eid] = managers[$manager][$managerSubarrays][type].subarray(from, to);
end = to;
}
cursors[type] = end;
managers[$manager][prop]._reset = eid => managers[$manager][prop][eid].fill(0);
managers[$manager][prop]._set = (eid, values) => managers[$manager][prop][eid].set(values, 0); // Object Type
} else if (typeof schema[prop] === 'object') {
managers[$manager][prop] = Manager(size, schema[prop], false); // String Type
} else if (typeof schema[prop] === 'string') {
const type = schema[prop];
const totalBytes = size * TYPES[type].BYTES_PER_ELEMENT;
const buffer = new ArrayBuffer(totalBytes);
const queryShadowBuffer = new ArrayBuffer(totalBytes);
const serializeShadowBuffer = new ArrayBuffer(totalBytes);
managers[$manager][prop] = new TYPES[type](buffer);
managers[$manager][prop][$queryShadow] = new TYPES[type](queryShadowBuffer);
managers[$manager][prop][$serializeShadow] = new TYPES[type](serializeShadowBuffer); // TypedArray Type
} else if (typeof schema[prop] === 'function') {
const Type = schema[prop];
const totalBytes = size * Type.BYTES_PER_ELEMENT;
const buffer = new ArrayBuffer(totalBytes);
managers[$manager][prop] = new Type(buffer);
} else {
throw new Error(`ECS Error: invalid property type ${schema[prop]}`);
}
}); // methods
Object.defineProperty(managers[$manager], '_schema', {
value: schema
});
Object.defineProperty(managers[$manager], '_mapping', {
value: prop => managers[$manager][$managerMaps][prop]
}); // Recursively set all values to 0
Object.defineProperty(managers[$manager], '_reset', {
value: eid => {
for (const prop of managers[$manager]._props) {
if (ArrayBuffer.isView(managers[$manager][prop])) {
if (ArrayBuffer.isView(managers[$manager][prop][eid])) {
managers[$manager][prop][eid].fill(0);
} else {
managers[$manager][prop][eid] = 0;
}
} else {
managers[$manager][prop]._reset(eid);
}
}
}
}); // Recursively set all values from a supplied object
Object.defineProperty(managers[$manager], '_set', {
value: (eid, values) => {
for (const prop in values) {
const mapping = managers[$manager]._mapping(prop);
if (mapping && typeof values[prop] === 'string') {
managers[$manager].enum(prop, eid, values[prop]);
} else if (ArrayBuffer.isView(managers[$manager][prop])) {
managers[$manager][prop][eid] = values[prop];
} else if (Array.isArray(values[prop]) && ArrayBuffer.isView(managers[$manager][prop][eid])) {
managers[$manager][prop][eid].set(values[prop], 0);
} else if (typeof managers[$manager][prop] === 'object') {
managers[$manager][prop]._set(eid, values[prop]);
}
}
}
});
Object.defineProperty(managers[$manager], '_get', {
value: eid => {
const obj = {};
for (const prop of managers[$manager]._props) {
const mapping = managers[$manager]._mapping(prop);
if (mapping) {
obj[prop] = managers[$manager].enum(prop, eid);
} else if (ArrayBuffer.isView(managers[$manager][prop])) {
obj[prop] = managers[$manager][prop][eid];
} else if (typeof managers[$manager][prop] === 'object') {
if (ArrayBuffer.isView(managers[$manager][prop][eid])) {
obj[prop] = Array.from(managers[$manager][prop][eid]);
} else {
obj[prop] = managers[$manager][prop]._get(eid);
}
}
}
return obj;
}
});
Object.defineProperty(managers[$manager], '_props', {
value: props
}); // Aggregate all typedArrays into single kvp array (memoized)
let flattened;
Object.defineProperty(managers[$manager], '_flatten', {
value: (flat = []) => {
if (flattened) return flattened;
for (const prop of managers[$manager]._props) {
if (ArrayBuffer.isView(managers[$manager][prop])) {
flat.push(managers[$manager][prop]);
} else if (typeof managers[$manager][prop] === 'object') {
managers[$manager][prop]._flatten(flat);
}
}
flattened = flat;
return flat;
}
});
Object.defineProperty(managers[$manager], 'enum', {
value: (prop, eid, value) => {
const mapping = managers[$manager]._mapping(prop);
if (!mapping) {
console.warn('Property is not an enum.');
return undefined;
}
if (value) {
const index = mapping.indexOf(value);
if (index === -1) {
console.warn(`Value '${value}' is not part of enum.`);
return undefined;
}
managers[$manager][prop][eid] = index;
} else {
return mapping[managers[$manager][prop][eid]];
}
}
});
Object.defineProperty(managers[$manager], '_grow', {
value: amount => {
managers[$manager][$managerSize] += amount;
for (const prop of managers[$manager]._props) {
if (ArrayBuffer.isView(managers[$manager][prop])) {
managers[$manager][prop] = grow(managers[$manager][prop], amount);
managers[$manager][prop][$queryShadow] = grow(managers[$manager][prop], amount);
} else if (typeof managers[$manager][prop] === 'object') {
if (ArrayBuffer.isView(managers[$manager][prop][eid])) ; else {
managers[$manager][prop]._grow();
}
}
}
}
});
return managers[$manager];
};
const $entityMasks = Symbol('entityMasks');
const $entityEnabled = Symbol('entityEnabled');
const $deferredEntityRemovals = Symbol('deferredEntityRemovals');
const $removedEntities = Symbol('removedEntities'); // 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
let globalEntityCursor = 0;
const getEntityCursor = () => globalEntityCursor;
const addEntity = world => {
const removed = world[$removedEntities];
const size = world[$size];
const enabled = world[$entityEnabled];
if (globalEntityCursor >= size - size / 5) {
// if 80% full
const amount = Math.ceil(size / 2 / 4) * 4; // grow by half the original size rounded up to a multiple of 4
// grow data stores
world[$componentMap].forEach(component => {
component.manager._grow(amount);
});
world[$size] += amount; // TODO: grow metadata on world mappings for queries/components enabled/etc
}
const eid = removed.length > 0 ? removed.pop() : globalEntityCursor;
enabled[eid] = 1;
globalEntityCursor++;
return eid;
};
const removeEntity = (world, eid) => world[$deferredEntityRemovals].push(eid);
const commitEntityRemovals = world => {
const deferred = world[$deferredEntityRemovals];
const queries = world[$queries];
const removed = world[$removedEntities];
const enabled = world[$entityEnabled];
for (let i = 0; i < deferred.length; i++) {
const eid = deferred[i]; // Check if entity is already removed
if (enabled[eid] === 0) continue; // Remove entity from all queries
// TODO: archetype graph
queries.forEach(query => {
queryRemoveEntity(query, eid);
}); // Free the entity
removed.push(eid);
enabled[eid] = 0; // Clear component bitmasks
for (let i = 0; i < world[$entityMasks].length; i++) world[$entityMasks][i][eid] = 0;
}
deferred.length = 0;
};
const diff = (world, query) => {
const q = world[$queryMap].get(query);
q.changed.length = 0;
const flat = q.flatProps;
for (let i = 0; i < q.entities.length; i++) {
const eid = q.entities[i];
let dirty = false;
for (let pid = 0; pid < flat.length; pid++) {
const prop = flat[pid];
if (ArrayBuffer.isView(prop[eid])) {
for (let i = 0; i < prop[eid].length; i++) {
if (prop[eid][i] !== prop[eid][$queryShadow][i]) {
dirty = true;
prop[eid][$queryShadow][i] = prop[eid][i];
}
}
} else {
if (prop[eid] !== prop[$queryShadow][eid]) {
dirty = true;
prop[$queryShadow][eid] = prop[eid];
}
}
}
if (dirty) q.changed.push(eid);
}
return q.changed;
};
const canonicalize = target => {
let componentProps;
let changedProps = new Set();
if (Array.isArray(target)) {
componentProps = target.map(p => {
if (p._flatten) {
return p._flatten();
} else if (typeof p === 'function' && p.name === 'QueryChanged') {
p = p();
if (p._flatten) {
let props = p._flatten();
props.forEach(x => changedProps.add(x));
return props;
}
changedProps.add(p);
return [p];
}
}).reduce((a, v) => a.concat(v), []);
} else {
target[$componentMap].forEach(c => {
componentProps = componentProps.concat(c._flatten());
});
}
return [componentProps, changedProps];
};
const defineSerializer = (target, maxBytes = 5000000) => {
const buffer = new ArrayBuffer(maxBytes);
const view = new DataView(buffer);
const [componentProps, changedProps] = canonicalize(target);
return ents => {
if (!ents.length) return;
let where = 0; // iterate over component props
for (let pid = 0; pid < componentProps.length; pid++) {
const prop = componentProps[pid];
const diff = changedProps.has(prop); // write pid
view.setUint8(where, pid);
where += 1; // save space for entity count
const countWhere = where;
where += 4;
let count = 0; // write eid,val
for (let i = 0; i < ents.length; i++) {
const eid = ents[i]; // skip if diffing and no change
if (diff && prop[eid] === prop[$serializeShadow][eid]) {
continue;
}
prop[$serializeShadow][eid] = prop[eid];
count++; // write eid
view.setUint32(where, eid);
where += 4; // if property is an array
if (ArrayBuffer.isView(prop[eid])) {
const type = prop[eid].constructor.name.replace('Array', '');
const indexType = prop[eid]._indexType;
const indexBytes = prop[eid]._indexBytes; // add space for count of dirty array elements
const countWhere2 = where;
where += 1;
let count2 = 0; // write array values
for (let i = 0; i < prop[eid].length; i++) {
const val = prop[eid][i]; // write array index
view[`set${indexType}`](where, i);
where += indexBytes; // write value at that index
view[`set${type}`](where, val);
where += prop[eid].BYTES_PER_ELEMENT;
count2++;
}
view[`set${indexType}`](countWhere2, count2);
} else {
// regular property values
const type = prop.constructor.name.replace('Array', ''); // set value next [type] bytes
view[`set${type}`](where, prop[eid]);
where += prop.BYTES_PER_ELEMENT;
}
}
view.setUint32(countWhere, count);
}
return buffer.slice(0, where);
};
};
const defineDeserializer = target => {
const [componentProps] = canonicalize(target);
return packet => {
const view = new DataView(packet);
let where = 0; // pid
const pid = view.getUint8(where);
where += 1; // entity count
const entityCount = view.getUint32(where);
where += 4; // typed array
const ta = componentProps[pid]; // Get the properties and set the new state
for (let i = 0; i < entityCount; i++) {
const eid = view.getUint32(where);
where += 4;
if (ArrayBuffer.isView(ta[eid])) {
const array = ta[eid];
const count = view[`get${array._indexType}`];
where += array._indexBytes; // iterate over count
for (let i = 0; i < count; i++) {
const value = view[`get${array.constructor.name.replace('Array', '')}`](where);
where += array.BYTES_PER_ELEMENT;
ta[eid][i] = value;
}
} else {
let value = view[`get${ta.constructor.name.replace('Array', '')}`](where);
where += ta.BYTES_PER_ELEMENT;
ta[eid] = value;
}
}
};
};
function Not(c) {
return function QueryNot() {
return c;
};
}
function Changed(c) {
return function QueryChanged() {
return c;
};
}
const $queries = Symbol('queries');
const $queryMap = Symbol('queryMap');
const $queryComponents = Symbol('queryComponents');
const enterQuery = (world, query, fn) => {
if (!world[$queryMap].get(query)) registerQuery(world, query);
world[$queryMap].get(query).enter = fn;
};
const exitQuery = (world, query, fn) => {
if (!world[$queryMap].get(query)) registerQuery(world, query);
world[$queryMap].get(query).exit = fn;
};
const registerQuery = (world, query) => {
if (!world[$queryMap].get(query)) world[$queryMap].set(query, {});
let components = [];
let notComponents = [];
let changedComponents = [];
query[$queryComponents].forEach(c => {
if (typeof c === 'function') {
if (c.name === 'QueryNot') {
notComponents.push(c());
}
if (c.name === 'QueryChanged') {
changedComponents.push(c());
components.push(c());
}
} else {
components.push(c);
}
});
const size = components.reduce((a, c) => c[$managerSize] > a ? c[$managerSize] : a, 0);
const entities = [];
const changed = [];
const indices = new Uint32Array(size);
const enabled = new Uint8Array(size);
const generations = components.map(c => world[$componentMap].get(c).generationId);
const mapComponents = c => world[$componentMap].get(c);
const reduceBitmasks = (a, c) => {
if (!a[c.generationId]) a[c.generationId] = 0;
a[c.generationId] |= c.bitflag;
return a;
};
const masks = components.map(mapComponents).reduce(reduceBitmasks, {});
const notMasks = notComponents.map(c => world[$componentMap].get(c)).reduce(reduceBitmasks, {});
const flatProps = components.map(c => !ArrayBuffer.isView(c) ? c._flatten() : [c]).reduce((a, v) => a.concat(v), []);
Object.assign(world[$queryMap].get(query), {
entities,
changed,
enabled,
components,
notComponents,
changedComponents,
masks,
notMasks,
generations,
indices,
flatProps
});
world[$queries].add(query);
for (let eid = 0; eid < getEntityCursor(); eid++) {
if (!world[$entityEnabled][eid]) continue;
if (queryCheckEntity(world, query, eid)) {
queryAddEntity(world, query, eid);
}
}
};
const defineQuery = components => {
const query = function (world) {
if (!world[$queryMap].has(query)) registerQuery(world, query);
const q = world[$queryMap].get(query);
if (q.changedComponents.length) return diff(world, query);
return q.entities;
};
query[$queryComponents] = components;
return query;
}; // TODO: archetype graph
const queryCheckEntity = (world, query, eid) => {
const {
masks,
notMasks,
generations
} = world[$queryMap].get(query);
for (let i = 0; i < generations.length; i++) {
const generationId = generations[i];
const qMask = masks[generationId];
const qNotMask = notMasks[generationId];
const eMask = world[$entityMasks][generationId][eid];
if ((eMask & qMask) !== qMask) {
return false;
} // if ((eMask | ~qNotMask) === ~qNotMask) {
// return false
// }
}
return true;
};
const queryCheckComponent = (world, query, component) => {
const {
generationId,
bitflag
} = world[$componentMap].get(component);
const {
masks
} = world[$queryMap].get(query);
const mask = masks[generationId];
return (mask & bitflag) === bitflag;
};
const queryCheckComponents = (world, query, components) => {
return components.every(c => queryCheckComponent(world, query, c));
};
const queryAddEntity = (world, query, eid) => {
const q = world[$queryMap].get(query);
if (q.enabled[eid]) return;
q.enabled[eid] = true;
q.entities.push(eid);
q.indices[eid] = q.entities.length - 1;
if (q.enter) q.enter(eid);
};
const queryRemoveEntity = (world, query, eid) => {
const q = world[$queryMap].get(query);
if (!q.enabled[eid]) return;
q.enabled[eid] = false;
q.entities.splice(q.indices[eid]);
if (q.exit) q.exit(eid);
};
const $componentMap = Symbol('componentMap');
const $deferredComponentRemovals = Symbol('de$deferredComponentRemovals');
const defineComponent = (schema, n) => alloc(schema, n);
const incrementBitflag = world => {
world[$bitflag] *= 2;
if (world[$bitflag] >= Math.pow(2, 32)) {
world[$bitflag] = 1;
world[$entityMasks].push(new Uint32Array(world[$size]));
}
};
const registerComponent = (world, component) => {
world[$componentMap].set(component, {
generationId: world[$entityMasks].length - 1,
bitflag: world[$bitflag],
manager: component
});
incrementBitflag(world);
};
const registerComponents = (world, components) => {
components.forEach(c => registerComponent(world, c));
};
const hasComponent = (world, component, eid) => {
const {
generationId,
bitflag
} = world[$componentMap].get(component);
const mask = world[$entityMasks][generationId][eid];
return (mask & bitflag) === bitflag;
};
const addComponent = (world, component, eid) => {
if (hasComponent(world, component, eid)) return; // Add bitflag to entity bitmask
const {
generationId,
bitflag
} = world[$componentMap].get(component);
world[$entityMasks][generationId][eid] |= bitflag; // Zero out each property value
// component._reset(eid)
// todo: archetype graph
const queries = world[$queries];
queries.forEach(query => {
const components = query[$queryComponents];
if (!queryCheckComponents(world, query, components)) return;
const match = queryCheckEntity(world, query, eid);
if (match) queryAddEntity(world, query, eid);
});
};
const removeComponent = (world, component, eid) => world[$deferredComponentRemovals].push(component, eid);
const commitComponentRemovals = world => {
const deferredComponentRemovals = world[$deferredComponentRemovals];
for (let i = 0; i < deferredComponentRemovals.length; i += 2) {
const component = deferredComponentRemovals[i];
const eid = deferredComponentRemovals[i + 1];
const {
generationId,
bitflag
} = world[$componentMap].get(component);
if (!(world[$entityMasks][generationId][eid] & bitflag)) return; // Remove flag from entity bitmask
world[$entityMasks][generationId][eid] &= ~bitflag; // todo: archetype graph
const queries = world[$queries];
queries.forEach(query => {
const components = query[$queryComponents];
if (!queryCheckComponents(world, query, components)) return;
const match = queryCheckEntity(world, query, eid);
if (match) queryRemoveEntity(world, query, eid);
});
}
deferredComponentRemovals.length = 0;
};
const $size = Symbol('size');
const $bitflag = Symbol('bitflag');
const createWorld = (size = 1000000) => {
const world = {};
world[$size] = size;
world[$entityEnabled] = new Uint8Array(world[$size]);
world[$entityMasks] = [new Uint32Array(size)];
world[$removedEntities] = [];
world[$bitflag] = 1;
world[$componentMap] = new Map();
world[$queryMap] = new Map();
world[$queries] = new Set();
world[$deferredComponentRemovals] = [];
world[$deferredEntityRemovals] = [];
return world;
};
const defineSystem = update => {
const system = world => {
update(world);
commitComponentRemovals(world);
commitEntityRemovals(world);
};
Object.defineProperty(system, 'name', {
value: (update.name || "AnonymousSystem") + "_internal",
configurable: true
});
return system;
};
const pipe = fns => world => {
for (let i = 0; i < fns.length; i++) {
const fn = fns[i];
fn(world);
}
};
const Types = TYPES_ENUM;
exports.Changed = Changed;
exports.Not = Not;
exports.Types = Types;
exports.addComponent = addComponent;
exports.addEntity = addEntity;
exports.createWorld = createWorld;
exports.defineComponent = defineComponent;
exports.defineDeserializer = defineDeserializer;
exports.defineQuery = defineQuery;
exports.defineSerializer = defineSerializer;
exports.defineSystem = defineSystem;
exports.enterQuery = enterQuery;
exports.exitQuery = exitQuery;
exports.pipe = pipe;
exports.registerComponent = registerComponent;
exports.registerComponents = registerComponents;
exports.removeComponent = removeComponent;
exports.removeEntity = removeEntity;
//# sourceMappingURL=index.min.js.map

2

package.json
{
"name": "bitecs",
"version": "0.2.8",
"version": "0.2.9",
"description": "Tiny, data-driven, high performance ECS library written in Javascript",

@@ -5,0 +5,0 @@ "license": "MPL-2.0",

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