@snapdm/model
Version:
An opinionated snapshot oriented modeling system for Cloud Firestore
202 lines (192 loc) • 6.71 kB
JavaScript
import { assertIsDefined, isUndefined, assert, isDefined } from '@snapdm/preconditions';
import __mergeWith from 'lodash/mergeWith';
let __adapter;
function adapter() {
assertIsDefined(__adapter, "Adapter not set. Call 'initialize' before creating models.");
return __adapter;
}
function setAdapter(adapter2) {
__adapter = adapter2;
}
function initialize(options) {
setAdapter(options.adapter);
}
function isSnapshot(value) {
const v = value;
return typeof v.type === "string" && typeof v.id === "string" && typeof v.ref !== "undefined" && typeof v.createdAt !== "undefined" && typeof v.updatedAt !== "undefined";
}
function delegate(target, delegateProperty) {
return new Proxy(target, {
get: (t, p) => {
const prop = coerceToString(p);
if (p in t) {
return t[prop];
}
return applyInScope(t[delegateProperty][prop], (thisArg) => thisArg === t ? t[delegateProperty] : thisArg);
}
});
}
function applyInScope(value, scope) {
if (typeof value === "function") {
return new Proxy(value, {
apply: (f, thisArg, args) => {
return f.apply(scope(thisArg), args);
}
});
}
return value;
}
function coerceToString(p) {
return typeof p === "string" ? p : p.toString();
}
function merge(...sources) {
return __mergeWith({}, ...sources, (obj, val) => Array.isArray(obj) ? val : void 0);
}
var __defProp$1 = Object.defineProperty;
var __getOwnPropSymbols$1 = Object.getOwnPropertySymbols;
var __hasOwnProp$1 = Object.prototype.hasOwnProperty;
var __propIsEnum$1 = Object.prototype.propertyIsEnumerable;
var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues$1 = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp$1.call(b, prop))
__defNormalProp$1(a, prop, b[prop]);
if (__getOwnPropSymbols$1)
for (var prop of __getOwnPropSymbols$1(b)) {
if (__propIsEnum$1.call(b, prop))
__defNormalProp$1(a, prop, b[prop]);
}
return a;
};
function combine(validators) {
return (value) => validators.reduce((res, next) => {
const errors = next(value);
if (errors) {
return res ? __spreadValues$1(__spreadValues$1({}, res), errors) : errors;
}
return res;
}, null);
}
var __defProp = Object.defineProperty;
var __defProps = Object.defineProperties;
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __propIsEnum = Object.prototype.propertyIsEnumerable;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp.call(b, prop))
__defNormalProp(a, prop, b[prop]);
if (__getOwnPropSymbols)
for (var prop of __getOwnPropSymbols(b)) {
if (__propIsEnum.call(b, prop))
__defNormalProp(a, prop, b[prop]);
}
return a;
};
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
function isExtendModelOptions(value) {
const v = value;
return typeof v.extends === "function";
}
class RootModel {
constructor(initializer, options) {
var _a;
if (isSnapshot(initializer)) {
this.isNew = (_a = options == null ? void 0 : options.isNew) != null ? _a : false;
this.updates = options == null ? void 0 : options.updates;
this.snapshot = initializer;
} else {
this.isNew = true;
this.snapshot = newSnapshot(this.constructor, initializer);
}
return delegate(this, "snapshot");
}
}
function Model(options) {
var _a;
const { type, initialize, validators } = options;
let baseClass;
let collection;
let parent;
let validator;
if (isExtendModelOptions(options)) {
baseClass = options.extends;
collection = options.extends.collection;
parent = options.extends.parent;
const extendValidator = options.extends.validator;
validator = validators ? combine([...validators, extendValidator]) : extendValidator;
} else {
baseClass = RootModel;
collection = options.collection;
parent = options.parent;
validator = validators ? combine(validators) : () => null;
}
return _a = class extends baseClass {
constructor(init, options2) {
super(init, options2);
this.model = this.constructor;
}
toRef(...includeAttributes) {
const { type: type2, id, ref } = this.snapshot;
return includeAttributes.map((k) => [k, this.snapshot[k]]).reduce((o, [k, v]) => {
o[k] = v;
return o;
}, { type: type2, id, ref });
}
clone(updates) {
if (updates === void 0 || Object.keys(updates).length === 0) {
return new this.model(__spreadValues({}, this.snapshot));
}
const computedUpdates = merge(this.updates, updates, {
updatedAt: adapter().fieldValues.serverTimestamp()
});
const newValue = merge(this.snapshot, computedUpdates);
const newEntity = new this.model(newValue, {
updates: computedUpdates,
isNew: this.isNew
});
return newEntity;
}
}, _a.type = type, _a.collection = collection, _a.parent = parent, _a.initialize = initialize != null ? initialize : identity, _a.validator = validator, _a;
}
function newSnapshot(type, init) {
const resource = type.initialize(init);
if (isUndefined(resource.type)) {
assertIsDefined(type.type, "must have modelType defined");
resource.type = type.type;
}
if (isUndefined(resource.id)) {
resource.id = adapter().ids();
}
const now = adapter().fieldValues.serverTimestamp();
return __spreadProps(__spreadValues({}, resource), {
ref: adapter().references(type.collection, resource.id, resolveParentRef(type, resource)),
createdAt: now,
updatedAt: now
});
}
function resolveParentRef(type, init) {
if (type.parent) {
const parentRef = init[type.parent.attribute];
assert(isModelRef(parentRef), `parent.attribute value '${String(type.parent.attribute)}' does not point to a ModelRef`);
return parentRef.ref;
}
return void 0;
}
function isModelRef(value) {
const v = value;
return typeof v.type === "string" && typeof v.id === "string" && isDefined(v.ref);
}
function identity(e) {
return e;
}
function buildModel(factory, data) {
return isModelClass(factory) ? new factory(data) : factory.factory(data);
}
function isModelClass(factory) {
return typeof factory === "function";
}
export { Model, adapter, buildModel, initialize };
//# sourceMappingURL=index.esm.js.map