@empirica/core
Version:
Empirica Core
458 lines (455 loc) • 11.7 kB
JavaScript
import {
error,
trace,
warn
} from "./chunk-TIKLWCJI.js";
// src/shared/attributes.ts
import { BehaviorSubject } from "rxjs";
var Attributes = class {
constructor(attributesObs, donesObs, setAttributes) {
this.setAttributes = setAttributes;
this.attrs = /* @__PURE__ */ new Map();
this.updates = /* @__PURE__ */ new Map();
attributesObs.subscribe({
next: ({ attribute, removed }) => {
this.update(attribute, removed);
}
});
donesObs.subscribe({
next: (scopeIDs) => {
this.next(scopeIDs);
}
});
}
attribute(scopeID, key) {
let scopeMap = this.attrs.get(scopeID);
if (!scopeMap) {
scopeMap = /* @__PURE__ */ new Map();
this.attrs.set(scopeID, scopeMap);
}
let attr = scopeMap.get(key);
if (!attr) {
attr = new Attribute(this.setAttributes, scopeID, key);
scopeMap.set(key, attr);
}
return attr;
}
attributes(scopeID) {
let scopeMap = this.attrs.get(scopeID);
if (!scopeMap) {
scopeMap = /* @__PURE__ */ new Map();
this.attrs.set(scopeID, scopeMap);
}
return Array.from(scopeMap.values());
}
attributePeek(scopeID, key) {
let scopeUpdateMap = this.updates.get(scopeID);
if (scopeUpdateMap) {
const updated = scopeUpdateMap.get(key);
if (updated) {
if (typeof updated === "boolean") {
return;
} else {
if (!updated.val) {
return;
} else {
const attr2 = new Attribute(this.setAttributes, scopeID, key);
attr2._update(updated);
return attr2;
}
}
}
}
let scopeMap = this.attrs.get(scopeID);
if (!scopeMap) {
return;
}
let attr = scopeMap.get(key);
if (!attr) {
return;
}
if (attr.value === void 0) {
return;
}
return attr;
}
nextAttributeValue(scopeID, key) {
const attr = this.attributePeek(scopeID, key);
if (!attr) {
return;
}
return attr.value;
}
update(attr, removed) {
let nodeID = attr.nodeID;
if (!nodeID) {
if (!attr.node?.id) {
error(`new attribute without node ID`);
return;
}
nodeID = attr.node.id;
}
let scopeMap = this.updates.get(nodeID);
if (!scopeMap) {
scopeMap = /* @__PURE__ */ new Map();
this.updates.set(nodeID, scopeMap);
}
if (removed) {
scopeMap.set(attr.key, true);
} else {
let key = attr.key;
if (attr.index !== void 0 && attr.index !== null) {
key = `${key}[${attr.index}]`;
}
scopeMap.set(key, attr);
}
}
scopeWasUpdated(scopeID) {
if (!scopeID) {
return false;
}
return this.updates.has(scopeID);
}
next(scopeIDs) {
for (const [scopeID, attrs] of this.updates) {
if (!scopeIDs.includes(scopeID)) {
continue;
}
let scopeMap = this.attrs.get(scopeID);
if (!scopeMap) {
scopeMap = /* @__PURE__ */ new Map();
this.attrs.set(scopeID, scopeMap);
}
for (const [key, attrOrDel] of attrs) {
if (typeof attrOrDel === "boolean") {
let attr = scopeMap.get(key);
if (attr) {
attr._update(void 0);
}
} else {
let attr = scopeMap.get(attrOrDel.key);
if (!attr) {
attr = new Attribute(this.setAttributes, scopeID, attrOrDel.key);
scopeMap.set(attrOrDel.key, attr);
}
attr._update(attrOrDel);
}
}
}
for (const scopeID of scopeIDs) {
this.updates.delete(scopeID);
}
}
};
var Attribute = class {
constructor(setAttributes, scopeID, key) {
this.setAttributes = setAttributes;
this.scopeID = scopeID;
this.key = key;
this.val = new BehaviorSubject(void 0);
}
get id() {
return this.attr?.id;
}
get createdAt() {
return this.attr ? new Date(this.attr.createdAt) : null;
}
get obs() {
return this.val;
}
get value() {
return this.val.getValue();
}
get nodeID() {
return this.scopeID;
}
// items returns the attribute changes for the current attribute, if it is a
// vector. Otherwise it returns null;
get items() {
if (!this.attrs) {
return null;
}
return this.attrs;
}
set(value, ao) {
const attrProps = this._prepSet(value, ao);
if (!attrProps) {
return;
}
this.setAttributes([attrProps]);
trace(`SET ${this.key} = ${value} (${this.scopeID})`);
}
_prepSet(value, ao, item) {
if (ao?.append !== void 0 && ao.index !== void 0) {
error(`cannot set both append and index`);
throw new Error(`cannot set both append and index`);
}
const serVal = JSON.stringify(value);
if (!item && (ao?.index !== void 0 || ao?.append)) {
let index = ao.index || 0;
if (ao?.append) {
index = this.attrs?.length || 0;
}
if (!this.attrs) {
this.attrs = [];
}
if (!this.attrs[index]) {
this.attrs[index] = new Attribute(
this.setAttributes,
this.scopeID,
this.key
);
} else {
const existing = this.attrs[index];
if (existing && existing.serVal === serVal) {
return;
}
}
this.attrs[index]._prepSet(value, ao, true);
const v = this._recalcVectorVal();
this.val.next(v);
} else {
if (this.serVal === serVal) {
return;
}
this.val.next(value);
}
this.serVal = serVal;
const attrProps = {
key: this.key,
nodeID: this.scopeID,
val: serVal
};
if (ao) {
attrProps.private = ao.private;
attrProps.protected = ao.protected;
attrProps.immutable = ao.immutable;
attrProps.ephemeral = ao.ephemeral;
attrProps.append = ao.append;
attrProps.index = ao.index;
}
return attrProps;
}
_recalcVectorVal() {
return this.attrs.map(
(a) => !a || a.val == void 0 ? null : a.value || null
);
}
// internal only
_update(attr, item) {
if (attr && this.attr && this.attr.id === attr.id) {
return;
}
if (attr && attr.vector && !item) {
if (attr.index === void 0) {
error(`vector attribute missing index`);
return;
}
if (this.attrs == void 0) {
this.attrs = [];
}
while (this.attrs.length < attr.index + 1) {
const newAttr2 = new Attribute(
this.setAttributes,
this.scopeID,
this.key
);
this.attrs.push(newAttr2);
}
const newAttr = new Attribute(this.setAttributes, this.scopeID, this.key);
newAttr._update(attr, true);
this.attrs[attr.index] = newAttr;
const value2 = this._recalcVectorVal();
this.val.next(value2);
return;
}
this.attr = attr;
this.serVal = attr?.val === void 0 || attr?.val === null ? "" : attr.val;
let value = void 0;
if (this.attr?.val) {
value = JSON.parse(this.attr.val);
}
this.val.next(value);
}
};
// src/shared/scopes.ts
import { BehaviorSubject as BehaviorSubject2 } from "rxjs";
var Scopes = class {
constructor(scopesObs, donesObs, ctx, kinds, attributes) {
this.ctx = ctx;
this.kinds = kinds;
this.attributes = attributes;
this.scopes = /* @__PURE__ */ new Map();
// newScopes is used to track scopes that have appeared for the first time.
this.newScopes = /* @__PURE__ */ new Map();
this.scopesByKind = /* @__PURE__ */ new Map();
this.kindUpdated = /* @__PURE__ */ new Set();
scopesObs.subscribe({
next: ({ scope, removed }) => {
this.update(scope, removed);
}
});
donesObs.subscribe({
next: (scopeIDs) => {
this.next(scopeIDs);
}
});
}
scope(id) {
return this.scopes.get(id)?.getValue();
}
scopeObs(id) {
return this.scopes.get(id);
}
byKind(kind) {
let map = this.scopesByKind.get(kind);
if (!map) {
map = /* @__PURE__ */ new Map();
this.scopesByKind.set(kind, map);
}
return map;
}
kindWasUpdated(kind) {
return this.kindUpdated.has(kind);
}
next(scopeIDs) {
this.kindUpdated.clear();
for (const [_, scopeSubject] of this.scopes) {
const scope = scopeSubject.getValue();
if ((scope._updated || this.attributes.scopeWasUpdated(scope.id)) && scopeIDs.includes(scope.id)) {
scope._updated = false;
scopeSubject.next(scope);
}
}
}
update(scope, removed) {
const existing = this.scopes.get(scope.id)?.getValue();
if (removed) {
if (!existing) {
warn("scopes: missing scope on removal", scope.id, scope.kind);
return;
}
existing._deleted = true;
existing._updated = true;
this.scopes.delete(scope.id);
if (!scope.kind) {
warn("scopes: scope missing kind on scope on removal");
return;
}
const kind2 = scope.kind;
this.scopesByKind.get(kind2).delete(scope.id);
this.kindUpdated.add(kind2);
return;
}
if (existing) {
existing._deleted = false;
return;
}
if (!scope.kind) {
warn("scopes: scope missing kind on scope");
return;
}
const kind = scope.kind;
const scopeClass = this.kinds[kind];
if (!scopeClass) {
warn(`scopes: unknown scope kind: ${scope.kind}`);
return;
}
const obj = this.create(scopeClass, scope);
const subj = new BehaviorSubject2(obj);
this.scopes.set(scope.id, subj);
this.newScopes.set(scope.id, true);
let skm = this.scopesByKind.get(kind);
if (!skm) {
skm = /* @__PURE__ */ new Map();
this.scopesByKind.set(kind, skm);
}
skm.set(scope.id, obj);
obj._updated = true;
this.kindUpdated.add(kind);
}
create(scopeClass, scope) {
return new scopeClass(this.ctx, scope, this.attributes);
}
};
var Scope = class {
constructor(ctx, scope, attributes) {
this.ctx = ctx;
this.scope = scope;
this.attributes = attributes;
/**
* @internal
*/
this._deleted = false;
/**
* @internal
*/
this._updated = false;
}
get id() {
return this.scope.id;
}
/**
* @internal
*/
get kind() {
return this.scope.kind;
}
get(key) {
return this.attributes.attribute(this.scope.id, key).value;
}
getAttribute(key) {
return this.attributes.attribute(this.scope.id, key);
}
obs(key) {
return this.attributes.attribute(this.scope.id, key).obs;
}
set(keyOrAttributes, value, ao) {
if (typeof keyOrAttributes === "string") {
if (value === void 0) {
value = null;
}
return this.attributes.attribute(this.scope.id, keyOrAttributes).set(value, ao);
}
const nextProps = [];
for (const attr of keyOrAttributes) {
const at = this.attributes.attribute(this.scope.id, attr.key)._prepSet(attr.value, attr.ao);
if (!at) {
continue;
}
nextProps.push(at);
}
if (nextProps.length === 0) {
return;
}
this.attributes.setAttributes(nextProps);
}
append(key, value, ao) {
if (!ao) {
ao = {};
}
ao.append = true;
return this.attributes.attribute(this.scope.id, key).set(value, ao);
}
inspect() {
const attrs = this.attributes.attributes(this.scope.id);
const out = {};
for (const attr of attrs) {
out[attr.key] = attr.value;
}
return out;
}
/**
* @internal
*/
hasUpdated() {
return this._updated || this.attributes.scopeWasUpdated(this.id);
}
};
export {
Attributes,
Attribute,
Scopes,
Scope
};
//# sourceMappingURL=chunk-RXGVZSIF.js.map