@jaysalvat/smart-model
Advanced tools
Comparing version 0.5.0 to 0.6.0
/**! | ||
* SmartModel | ||
* Javascript object model | ||
* https://github.com/jaysalvat/smart-model | ||
* @version 0.5.0 built 2021-02-25 09:18:46 | ||
* @version 0.6.0 built 2021-02-26 08:03:42 | ||
* @license ISC | ||
@@ -30,3 +29,3 @@ * @author Jay Salvat http://jaysalvat.com | ||
function isSmartModel(value) { | ||
return value.prototype instanceof SmartModel || value instanceof SmartModel; | ||
return value && (value.prototype instanceof SmartModel || value instanceof SmartModel); | ||
} | ||
@@ -43,3 +42,3 @@ | ||
function keys(obj, cb = function() {}) { | ||
return Object.keys(obj).map(cb); | ||
return Object.keys(obj).map((key => cb(key, obj[key]))); | ||
} | ||
@@ -153,4 +152,3 @@ | ||
if (entry.rule) { | ||
keys(entry.rule, (key => { | ||
const rule = entry.rule[key]; | ||
keys(entry.rule, ((key, rule) => { | ||
if (rule(value)) { | ||
@@ -200,3 +198,3 @@ errors.push({ | ||
} | ||
if (isTypeArrayOfSmartModels(entry.type)) { | ||
if (isTypeArrayOfSmartModels(entry.type) && !isUndef(value)) { | ||
value = entry.type[0].$hydrate(value); | ||
@@ -216,3 +214,3 @@ entry.type = Array; | ||
} | ||
if (Nested) { | ||
if (Nested && !isUndef(value)) { | ||
value = new Nested(value); | ||
@@ -224,2 +222,3 @@ } | ||
trigger(target.$onUpdate); | ||
target.$applySubscribers(property, value); | ||
} | ||
@@ -262,6 +261,9 @@ return true; | ||
} | ||
trigger(target.$onBeforeDelete); | ||
if (trigger(target.$onBeforeDelete) === false) { | ||
return true; | ||
} | ||
Reflect.deleteProperty(target, property); | ||
trigger(target.$onDelete); | ||
trigger(target.$onUpdate); | ||
target.$applySubscribers(property, value); | ||
return true; | ||
@@ -276,12 +278,3 @@ } | ||
super(schema, settings); | ||
keys(schema, (key => { | ||
if (isUndef(data[key])) { | ||
if (!isUndef(schema[key].default)) { | ||
this[key] = schema[key].default; | ||
} else if (!isFn(schema[key])) { | ||
this[key] = data[key]; | ||
} | ||
} | ||
})); | ||
this.$patch(data); | ||
this.$post(data); | ||
} | ||
@@ -293,22 +286,23 @@ $patch(data) { | ||
} | ||
$put(data) { | ||
keys(this, (key => { | ||
if (data[key]) { | ||
if (isSmartModel(this[key])) { | ||
this[key].$put(data[key]); | ||
$post(data) { | ||
let undef; | ||
const schema = this.$schema(); | ||
keys(schema, ((key, value) => { | ||
if (isUndef(data[key])) { | ||
if (!isUndef(value.default)) { | ||
this[key] = value.default; | ||
} else { | ||
this[key] = data[key]; | ||
this[key] = undef; | ||
} | ||
} else { | ||
this.$delete(key); | ||
} | ||
})); | ||
keys(data, (key => { | ||
if (!this[key]) { | ||
this[key] = data[key]; | ||
keys(this, ((key, value) => { | ||
if (isUndef(schema[key] && isUndef(value))) { | ||
this.$delete(key); | ||
} | ||
})); | ||
this.$patch(data); | ||
} | ||
$post(data) { | ||
return this.$put(data); | ||
$put(data) { | ||
return this.$post(data); | ||
} | ||
@@ -320,2 +314,8 @@ $delete(properties) { | ||
} | ||
$subscribe(fn) { | ||
this.$subscribers().push(fn); | ||
return () => { | ||
this.$subscribers(fn); | ||
}; | ||
} | ||
} | ||
@@ -346,2 +346,3 @@ | ||
SmartModel.create = function(name, schema, settings) { | ||
let subscribers = []; | ||
settings = merge(SmartModel.settings, settings); | ||
@@ -353,10 +354,23 @@ const Model = { | ||
} | ||
$schema() { | ||
return schema; | ||
} | ||
$subscribers(removedFn) { | ||
if (removedFn) { | ||
subscribers = subscribers.filter((fn => fn !== removedFn)); | ||
} | ||
return subscribers; | ||
} | ||
$applySubscribers(property, value) { | ||
keys(subscribers, ((_, subscriber) => { | ||
Reflect.apply(subscriber, this, [ property, value, this ]); | ||
})); | ||
} | ||
} | ||
}[name]; | ||
Model.$check = function(payload, filters) { | ||
Model.$check = function(payload = {}, filters) { | ||
const invalidations = {}; | ||
keys(schema, (property => { | ||
keys(schema, ((property, entry) => { | ||
let subErrors; | ||
const value = payload[property]; | ||
const entry = schema[property]; | ||
const Nested = createNested(entry, property, settings); | ||
@@ -388,3 +402,2 @@ if (Nested) { | ||
Object.assign(Model.prototype, settings.methods); | ||
Model.schema = schema; | ||
return Model; | ||
@@ -391,0 +404,0 @@ }; |
@@ -1,2 +0,2 @@ | ||
/*! SmartModel v0.5.0 */ | ||
function e(e){return Array.isArray(e)}function t(e){return void 0===e}function r(e){return"function"==typeof e}function n(e){return e&&e.toString().startsWith("class")}function o(e){return e.prototype instanceof l||e instanceof l}function s(e){return e&&"[object Object]"===e.toString()}function c(e,t=function(){}){return Object.keys(e).map(t)}function i(e){return[].concat([],e)}function u(e,t){return t=Object.assign({},e,t),c(e,(r=>{s(e[r])&&s(t[r])&&(t[r]=Object.assign({},e[r],u(e[r],t[r])))})),t}class a extends Error{constructor(e){super(e.message),Object.assign(this,e)}}function p(t,r,u,a,p){const f=[];return p.strict&&!c(t||{}).length&&f.push({message:`Property "${r}" can't be set in strict mode`,code:"strict"}),t.required&&p.empty(u)?(f.push({message:`Property "${r}" is "required"`,code:"required"}),f):t.readonly&&!a?(f.push({message:`Property "${r}" is "readonly"`,code:"readonly"}),f):(void 0===u||(!t.type||!t.required&&p.empty(u)||o(t.type)&&s(u)||i(t.type).some((t=>function(t,r){const o=r&&r.toString().match(/^\s*function (\w+)/),s=(o?o[1]:"object").toLowerCase();if("date"===s&&t instanceof r)return!0;if("array"===s&&e(t))return!0;if("object"===s){if(n(r)&&t instanceof r)return!0;if(!n(r)&&typeof t===s)return!0}else if(typeof t===s)return!0;return!1}(u,t)))||f.push({message:`Property "${r}" has an invalid type "${typeof u}"`,code:"type"}),t.rule&&c(t.rule,(e=>{(0,t.rule[e])(u)&&f.push({message:`Property "${r}" triggers the "${e}" rule error`,code:"rule:"+e})}))),f)}function f(e={},t,r){if(e.type){const n=!!o(e.type)&&e.type,c=!!s(e.type)&&e.type;if(n||c){const o=n||l.create(t.normalize("NFD").replace(/[\u0300-\u036f]/g,"").match(/[a-z1-9]+/gi).map((e=>e.charAt(0).toUpperCase()+e.substr(1).toLowerCase())).join(""),c,r);return e.type=o,o}}return!1}a.throw=function(e,t,r,n,o){const c=t.split(":")[0];if(o=o&&o.constructor.name,!0===e.exceptions||s(e.exceptions)&&e.exceptions[c])throw new a({message:`[${o}] ${r}`,source:o,property:n,code:t})};class l extends class{constructor(n,s){return new Proxy(this,{set(i,u,l){const y=n[u]||{},d=i[u],h=t(d),$=!(h||(g=l,m=d,JSON.stringify(g)===JSON.stringify(m)));var g,m;const b=f(y,u,s);function w(e,r){const o=Reflect.apply(e,i,r||[u,l,d,n]);return t(o)?l:o}var j;l=w(i.$onBeforeSet),$&&(l=w(i.$onBeforeUpdate)),r(y.transform)&&(l=w(y.transform,[l,n])),e(j=y.type)&&1===j.length&&o(j[0])&&(l=y.type[0].$hydrate(l),y.type=Array);const x=p(y,u,l,h,s);if(x.length){if(!s.exceptions)return!0;a.throw(s,x[0].code,x[0].message,u,i)}return s.strict&&!c(y).length||(b&&(l=new b(l)),i[u]=l,w(i.$onSet),$&&w(i.$onUpdate)),!0},get(e,s){const i=n[s];let u=e[s];if(["$get"].includes(s))return function(){return function(e){return c(e=Object.assign({},e),(t=>{o(e[t])&&(e[t]=e[t].$get())})),e}(e)};if(!i)return e[s];function a(r,o){const c=Reflect.apply(r,e,o||[s,u,n]);return t(c)?u:c}return u=a(e.$onBeforeGet),r(i)&&(u=a(i,[e,n])),r(i.format)&&(u=a(i.format,[u,n])),u=a(e.$onGet),u},deleteProperty(e,t){const r=e[t];function o(o,s){return Reflect.apply(o,e,s||[t,r,n])}return(n[t]||{}).required&&a.throw(s,"required",`Property "${t}" is "required"`,t,e),o(e.$onBeforeDelete),Reflect.deleteProperty(e,t),o(e.$onDelete),o(e.$onUpdate),!0}})}}{constructor(e={},n={},o){super(e,o),c(e,(o=>{t(n[o])&&(t(e[o].default)?r(e[o])||(this[o]=n[o]):this[o]=e[o].default)})),this.$patch(n)}$patch(e){c(e,(t=>{this[t]=e[t]}))}$put(e){c(this,(t=>{e[t]?o(this[t])?this[t].$put(e[t]):this[t]=e[t]:this.$delete(t)})),c(e,(t=>{this[t]||(this[t]=e[t])}))}$post(e){return this.$put(e)}$delete(e){i(e).forEach((e=>{Reflect.deleteProperty(this,e)}))}}l.settings={empty:e=>""===e||null===e||t(e),strict:!1,exceptions:{readonly:!1,required:!0,rule:!0,strict:!1,type:!0},methods:{$onBeforeGet:()=>{},$onBeforeSet:()=>{},$onBeforeUpdate:()=>{},$onDelete:()=>{},$onGet:()=>{},$onBeforeDelete:()=>{},$onSet:()=>{},$onUpdate:()=>{}}},l.create=function(t,r,n){n=u(l.settings,n);const o={[t]:class extends l{constructor(e){super(r,e,n)}}}[t];return o.$check=function(e,t){const o={};return c(r,(s=>{let c;const u=e[s],a=r[s],l=f(a,s,n);l&&(c=l.$check(u,t));let y=p(a,s,u,!1,n);c?o[s]=c:y.length&&(t&&(y=y.filter((e=>!i(t).includes(e.code)))),y.length&&(o[s]=y))})),!!c(o).length&&o},o.$hydrate=function(t){return e(t)?t.map((e=>new o(e))):new o(t)},Object.assign(o.prototype,n.methods),o.schema=r,o};export default l; | ||
/*! SmartModel v0.6.0 */ | ||
function e(e){return Array.isArray(e)}function t(e){return void 0===e}function r(e){return"function"==typeof e}function n(e){return e&&e.toString().startsWith("class")}function s(e){return e&&(e.prototype instanceof l||e instanceof l)}function o(e){return e&&"[object Object]"===e.toString()}function c(e,t=function(){}){return Object.keys(e).map((r=>t(r,e[r])))}function i(e){return[].concat([],e)}function u(e,t){return t=Object.assign({},e,t),c(e,(r=>{o(e[r])&&o(t[r])&&(t[r]=Object.assign({},e[r],u(e[r],t[r])))})),t}class p extends Error{constructor(e){super(e.message),Object.assign(this,e)}}function a(t,r,u,p,a){const f=[];return a.strict&&!c(t||{}).length&&f.push({message:`Property "${r}" can't be set in strict mode`,code:"strict"}),t.required&&a.empty(u)?(f.push({message:`Property "${r}" is "required"`,code:"required"}),f):t.readonly&&!p?(f.push({message:`Property "${r}" is "readonly"`,code:"readonly"}),f):(void 0===u||(!t.type||!t.required&&a.empty(u)||s(t.type)&&o(u)||i(t.type).some((t=>function(t,r){const s=r&&r.toString().match(/^\s*function (\w+)/),o=(s?s[1]:"object").toLowerCase();if("date"===o&&t instanceof r)return!0;if("array"===o&&e(t))return!0;if("object"===o){if(n(r)&&t instanceof r)return!0;if(!n(r)&&typeof t===o)return!0}else if(typeof t===o)return!0;return!1}(u,t)))||f.push({message:`Property "${r}" has an invalid type "${typeof u}"`,code:"type"}),t.rule&&c(t.rule,((e,t)=>{t(u)&&f.push({message:`Property "${r}" triggers the "${e}" rule error`,code:"rule:"+e})}))),f)}function f(e={},t,r){if(e.type){const n=!!s(e.type)&&e.type,c=!!o(e.type)&&e.type;if(n||c){const s=n||l.create(t.normalize("NFD").replace(/[\u0300-\u036f]/g,"").match(/[a-z1-9]+/gi).map((e=>e.charAt(0).toUpperCase()+e.substr(1).toLowerCase())).join(""),c,r);return e.type=s,s}}return!1}p.throw=function(e,t,r,n,s){const c=t.split(":")[0];if(s=s&&s.constructor.name,!0===e.exceptions||o(e.exceptions)&&e.exceptions[c])throw new p({message:`[${s}] ${r}`,source:s,property:n,code:t})};class l extends class{constructor(n,o){return new Proxy(this,{set(i,u,l){const y=n[u]||{},$=i[u],h=t($),d=!(h||(g=l,m=$,JSON.stringify(g)===JSON.stringify(m)));var g,m;const b=f(y,u,o);function w(e,r){const s=Reflect.apply(e,i,r||[u,l,$,n]);return t(s)?l:s}var S;l=w(i.$onBeforeSet),d&&(l=w(i.$onBeforeUpdate)),r(y.transform)&&(l=w(y.transform,[l,n])),e(S=y.type)&&1===S.length&&s(S[0])&&!t(l)&&(l=y.type[0].$hydrate(l),y.type=Array);const j=a(y,u,l,h,o);if(j.length){if(!o.exceptions)return!0;p.throw(o,j[0].code,j[0].message,u,i)}return o.strict&&!c(y).length||(b&&!t(l)&&(l=new b(l)),i[u]=l,w(i.$onSet),d&&(w(i.$onUpdate),i.$applySubscribers(u,l))),!0},get(e,o){const i=n[o];let u=e[o];if(["$get"].includes(o))return function(){return function(e){return c(e=Object.assign({},e),(t=>{s(e[t])&&(e[t]=e[t].$get())})),e}(e)};if(!i)return e[o];function p(r,s){const c=Reflect.apply(r,e,s||[o,u,n]);return t(c)?u:c}return u=p(e.$onBeforeGet),r(i)&&(u=p(i,[e,n])),r(i.format)&&(u=p(i.format,[u,n])),u=p(e.$onGet),u},deleteProperty(e,t){const r=e[t];function s(s,o){return Reflect.apply(s,e,o||[t,r,n])}return(n[t]||{}).required&&p.throw(o,"required",`Property "${t}" is "required"`,t,e),!1===s(e.$onBeforeDelete)||(Reflect.deleteProperty(e,t),s(e.$onDelete),s(e.$onUpdate),e.$applySubscribers(t,r)),!0}})}}{constructor(e={},t={},r){super(e,r),this.$post(t)}$patch(e){c(e,(t=>{this[t]=e[t]}))}$post(e){const r=this.$schema();c(r,((r,n)=>{t(e[r])&&(this[r]=t(n.default)?void 0:n.default)})),c(this,((e,n)=>{t(r[e]&&t(n))&&this.$delete(e)})),this.$patch(e)}$put(e){return this.$post(e)}$delete(e){i(e).forEach((e=>{Reflect.deleteProperty(this,e)}))}$subscribe(e){return this.$subscribers().push(e),()=>{this.$subscribers(e)}}}l.settings={empty:e=>""===e||null===e||t(e),strict:!1,exceptions:{readonly:!1,required:!0,rule:!0,strict:!1,type:!0},methods:{$onBeforeGet:()=>{},$onBeforeSet:()=>{},$onBeforeUpdate:()=>{},$onDelete:()=>{},$onGet:()=>{},$onBeforeDelete:()=>{},$onSet:()=>{},$onUpdate:()=>{}}},l.create=function(t,r,n){let s=[];n=u(l.settings,n);const o={[t]:class extends l{constructor(e){super(r,e,n)}$schema(){return r}$subscribers(e){return e&&(s=s.filter((t=>t!==e))),s}$applySubscribers(e,t){c(s,((r,n)=>{Reflect.apply(n,this,[e,t,this])}))}}}[t];return o.$check=function(e={},t){const s={};return c(r,((r,o)=>{let c;const u=e[r],p=f(o,r,n);p&&(c=p.$check(u,t));let l=a(o,r,u,!1,n);c?s[r]=c:l.length&&(t&&(l=l.filter((e=>!i(t).includes(e.code)))),l.length&&(s[r]=l))})),!!c(s).length&&s},o.$hydrate=function(t){return e(t)?t.map((e=>new o(e))):new o(t)},Object.assign(o.prototype,n.methods),o};export default l; |
/**! | ||
* SmartModel | ||
* Javascript object model | ||
* https://github.com/jaysalvat/smart-model | ||
* @version 0.5.0 built 2021-02-25 09:18:46 | ||
* @version 0.6.0 built 2021-02-26 08:03:42 | ||
* @license ISC | ||
@@ -27,3 +26,3 @@ * @author Jay Salvat http://jaysalvat.com | ||
function isSmartModel(value) { | ||
return value.prototype instanceof SmartModel || value instanceof SmartModel; | ||
return value && (value.prototype instanceof SmartModel || value instanceof SmartModel); | ||
} | ||
@@ -37,3 +36,3 @@ function isPlainObject(value) { | ||
function keys(obj, cb = function() {}) { | ||
return Object.keys(obj).map(cb); | ||
return Object.keys(obj).map((key => cb(key, obj[key]))); | ||
} | ||
@@ -139,4 +138,3 @@ function toArray(value) { | ||
if (entry.rule) { | ||
keys(entry.rule, (key => { | ||
const rule = entry.rule[key]; | ||
keys(entry.rule, ((key, rule) => { | ||
if (rule(value)) { | ||
@@ -184,3 +182,3 @@ errors.push({ | ||
} | ||
if (isTypeArrayOfSmartModels(entry.type)) { | ||
if (isTypeArrayOfSmartModels(entry.type) && !isUndef(value)) { | ||
value = entry.type[0].$hydrate(value); | ||
@@ -200,3 +198,3 @@ entry.type = Array; | ||
} | ||
if (Nested) { | ||
if (Nested && !isUndef(value)) { | ||
value = new Nested(value); | ||
@@ -208,2 +206,3 @@ } | ||
trigger(target.$onUpdate); | ||
target.$applySubscribers(property, value); | ||
} | ||
@@ -246,6 +245,9 @@ return true; | ||
} | ||
trigger(target.$onBeforeDelete); | ||
if (trigger(target.$onBeforeDelete) === false) { | ||
return true; | ||
} | ||
Reflect.deleteProperty(target, property); | ||
trigger(target.$onDelete); | ||
trigger(target.$onUpdate); | ||
target.$applySubscribers(property, value); | ||
return true; | ||
@@ -259,12 +261,3 @@ } | ||
super(schema, settings); | ||
keys(schema, (key => { | ||
if (isUndef(data[key])) { | ||
if (!isUndef(schema[key].default)) { | ||
this[key] = schema[key].default; | ||
} else if (!isFn(schema[key])) { | ||
this[key] = data[key]; | ||
} | ||
} | ||
})); | ||
this.$patch(data); | ||
this.$post(data); | ||
} | ||
@@ -276,22 +269,23 @@ $patch(data) { | ||
} | ||
$put(data) { | ||
keys(this, (key => { | ||
if (data[key]) { | ||
if (isSmartModel(this[key])) { | ||
this[key].$put(data[key]); | ||
$post(data) { | ||
let undef; | ||
const schema = this.$schema(); | ||
keys(schema, ((key, value) => { | ||
if (isUndef(data[key])) { | ||
if (!isUndef(value.default)) { | ||
this[key] = value.default; | ||
} else { | ||
this[key] = data[key]; | ||
this[key] = undef; | ||
} | ||
} else { | ||
this.$delete(key); | ||
} | ||
})); | ||
keys(data, (key => { | ||
if (!this[key]) { | ||
this[key] = data[key]; | ||
keys(this, ((key, value) => { | ||
if (isUndef(schema[key] && isUndef(value))) { | ||
this.$delete(key); | ||
} | ||
})); | ||
this.$patch(data); | ||
} | ||
$post(data) { | ||
return this.$put(data); | ||
$put(data) { | ||
return this.$post(data); | ||
} | ||
@@ -303,2 +297,8 @@ $delete(properties) { | ||
} | ||
$subscribe(fn) { | ||
this.$subscribers().push(fn); | ||
return () => { | ||
this.$subscribers(fn); | ||
}; | ||
} | ||
} | ||
@@ -327,2 +327,3 @@ SmartModel.settings = { | ||
SmartModel.create = function(name, schema, settings) { | ||
let subscribers = []; | ||
settings = merge(SmartModel.settings, settings); | ||
@@ -334,10 +335,23 @@ const Model = { | ||
} | ||
$schema() { | ||
return schema; | ||
} | ||
$subscribers(removedFn) { | ||
if (removedFn) { | ||
subscribers = subscribers.filter((fn => fn !== removedFn)); | ||
} | ||
return subscribers; | ||
} | ||
$applySubscribers(property, value) { | ||
keys(subscribers, ((_, subscriber) => { | ||
Reflect.apply(subscriber, this, [ property, value, this ]); | ||
})); | ||
} | ||
} | ||
}[name]; | ||
Model.$check = function(payload, filters) { | ||
Model.$check = function(payload = {}, filters) { | ||
const invalidations = {}; | ||
keys(schema, (property => { | ||
keys(schema, ((property, entry) => { | ||
let subErrors; | ||
const value = payload[property]; | ||
const entry = schema[property]; | ||
const Nested = createNested(entry, property, settings); | ||
@@ -369,3 +383,2 @@ if (Nested) { | ||
Object.assign(Model.prototype, settings.methods); | ||
Model.schema = schema; | ||
return Model; | ||
@@ -372,0 +385,0 @@ }; |
@@ -1,2 +0,2 @@ | ||
/*! SmartModel v0.5.0 */ | ||
var SmartModel=function(){"use strict";function e(e){return Array.isArray(e)}function t(e){return void 0===e}function r(e){return"function"==typeof e}function n(e){return e&&e.toString().startsWith("class")}function o(e){return e.prototype instanceof l||e instanceof l}function s(e){return e&&"[object Object]"===e.toString()}function c(e,t=function(){}){return Object.keys(e).map(t)}function i(e){return[].concat([],e)}function u(e,t){return t=Object.assign({},e,t),c(e,(r=>{s(e[r])&&s(t[r])&&(t[r]=Object.assign({},e[r],u(e[r],t[r])))})),t}class a extends Error{constructor(e){super(e.message),Object.assign(this,e)}}function p(t,r,u,a,p){const f=[];return p.strict&&!c(t||{}).length&&f.push({message:`Property "${r}" can't be set in strict mode`,code:"strict"}),t.required&&p.empty(u)?(f.push({message:`Property "${r}" is "required"`,code:"required"}),f):t.readonly&&!a?(f.push({message:`Property "${r}" is "readonly"`,code:"readonly"}),f):(void 0===u||(!t.type||!t.required&&p.empty(u)||o(t.type)&&s(u)||i(t.type).some((t=>function(t,r){const o=r&&r.toString().match(/^\s*function (\w+)/),s=(o?o[1]:"object").toLowerCase();if("date"===s&&t instanceof r)return!0;if("array"===s&&e(t))return!0;if("object"===s){if(n(r)&&t instanceof r)return!0;if(!n(r)&&typeof t===s)return!0}else if(typeof t===s)return!0;return!1}(u,t)))||f.push({message:`Property "${r}" has an invalid type "${typeof u}"`,code:"type"}),t.rule&&c(t.rule,(e=>{(0,t.rule[e])(u)&&f.push({message:`Property "${r}" triggers the "${e}" rule error`,code:"rule:"+e})}))),f)}function f(e={},t,r){if(e.type){const n=!!o(e.type)&&e.type,c=!!s(e.type)&&e.type;if(n||c){const o=n||l.create(t.normalize("NFD").replace(/[\u0300-\u036f]/g,"").match(/[a-z1-9]+/gi).map((e=>e.charAt(0).toUpperCase()+e.substr(1).toLowerCase())).join(""),c,r);return e.type=o,o}}return!1}a.throw=function(e,t,r,n,o){const c=t.split(":")[0];if(o=o&&o.constructor.name,!0===e.exceptions||s(e.exceptions)&&e.exceptions[c])throw new a({message:`[${o}] ${r}`,source:o,property:n,code:t})};class l extends class{constructor(n,s){return new Proxy(this,{set(i,u,l){const y=n[u]||{},d=i[u],h=t(d),$=!(h||(g=l,m=d,JSON.stringify(g)===JSON.stringify(m)));var g,m;const b=f(y,u,s);function w(e,r){const o=Reflect.apply(e,i,r||[u,l,d,n]);return t(o)?l:o}var j;l=w(i.$onBeforeSet),$&&(l=w(i.$onBeforeUpdate)),r(y.transform)&&(l=w(y.transform,[l,n])),e(j=y.type)&&1===j.length&&o(j[0])&&(l=y.type[0].$hydrate(l),y.type=Array);const P=p(y,u,l,h,s);if(P.length){if(!s.exceptions)return!0;a.throw(s,P[0].code,P[0].message,u,i)}return s.strict&&!c(y).length||(b&&(l=new b(l)),i[u]=l,w(i.$onSet),$&&w(i.$onUpdate)),!0},get(e,s){const i=n[s];let u=e[s];if(["$get"].includes(s))return function(){return function(e){return c(e=Object.assign({},e),(t=>{o(e[t])&&(e[t]=e[t].$get())})),e}(e)};if(!i)return e[s];function a(r,o){const c=Reflect.apply(r,e,o||[s,u,n]);return t(c)?u:c}return u=a(e.$onBeforeGet),r(i)&&(u=a(i,[e,n])),r(i.format)&&(u=a(i.format,[u,n])),u=a(e.$onGet),u},deleteProperty(e,t){const r=e[t];function o(o,s){return Reflect.apply(o,e,s||[t,r,n])}return(n[t]||{}).required&&a.throw(s,"required",`Property "${t}" is "required"`,t,e),o(e.$onBeforeDelete),Reflect.deleteProperty(e,t),o(e.$onDelete),o(e.$onUpdate),!0}})}}{constructor(e={},n={},o){super(e,o),c(e,(o=>{t(n[o])&&(t(e[o].default)?r(e[o])||(this[o]=n[o]):this[o]=e[o].default)})),this.$patch(n)}$patch(e){c(e,(t=>{this[t]=e[t]}))}$put(e){c(this,(t=>{e[t]?o(this[t])?this[t].$put(e[t]):this[t]=e[t]:this.$delete(t)})),c(e,(t=>{this[t]||(this[t]=e[t])}))}$post(e){return this.$put(e)}$delete(e){i(e).forEach((e=>{Reflect.deleteProperty(this,e)}))}}return l.settings={empty:e=>""===e||null===e||t(e),strict:!1,exceptions:{readonly:!1,required:!0,rule:!0,strict:!1,type:!0},methods:{$onBeforeGet:()=>{},$onBeforeSet:()=>{},$onBeforeUpdate:()=>{},$onDelete:()=>{},$onGet:()=>{},$onBeforeDelete:()=>{},$onSet:()=>{},$onUpdate:()=>{}}},l.create=function(t,r,n){n=u(l.settings,n);const o={[t]:class extends l{constructor(e){super(r,e,n)}}}[t];return o.$check=function(e,t){const o={};return c(r,(s=>{let c;const u=e[s],a=r[s],l=f(a,s,n);l&&(c=l.$check(u,t));let y=p(a,s,u,!1,n);c?o[s]=c:y.length&&(t&&(y=y.filter((e=>!i(t).includes(e.code)))),y.length&&(o[s]=y))})),!!c(o).length&&o},o.$hydrate=function(t){return e(t)?t.map((e=>new o(e))):new o(t)},Object.assign(o.prototype,n.methods),o.schema=r,o},l}(); | ||
/*! SmartModel v0.6.0 */ | ||
var SmartModel=function(){"use strict";function e(e){return Array.isArray(e)}function t(e){return void 0===e}function r(e){return"function"==typeof e}function n(e){return e&&e.toString().startsWith("class")}function s(e){return e&&(e.prototype instanceof l||e instanceof l)}function o(e){return e&&"[object Object]"===e.toString()}function c(e,t=function(){}){return Object.keys(e).map((r=>t(r,e[r])))}function i(e){return[].concat([],e)}function u(e,t){return t=Object.assign({},e,t),c(e,(r=>{o(e[r])&&o(t[r])&&(t[r]=Object.assign({},e[r],u(e[r],t[r])))})),t}class p extends Error{constructor(e){super(e.message),Object.assign(this,e)}}function a(t,r,u,p,a){const f=[];return a.strict&&!c(t||{}).length&&f.push({message:`Property "${r}" can't be set in strict mode`,code:"strict"}),t.required&&a.empty(u)?(f.push({message:`Property "${r}" is "required"`,code:"required"}),f):t.readonly&&!p?(f.push({message:`Property "${r}" is "readonly"`,code:"readonly"}),f):(void 0===u||(!t.type||!t.required&&a.empty(u)||s(t.type)&&o(u)||i(t.type).some((t=>function(t,r){const s=r&&r.toString().match(/^\s*function (\w+)/),o=(s?s[1]:"object").toLowerCase();if("date"===o&&t instanceof r)return!0;if("array"===o&&e(t))return!0;if("object"===o){if(n(r)&&t instanceof r)return!0;if(!n(r)&&typeof t===o)return!0}else if(typeof t===o)return!0;return!1}(u,t)))||f.push({message:`Property "${r}" has an invalid type "${typeof u}"`,code:"type"}),t.rule&&c(t.rule,((e,t)=>{t(u)&&f.push({message:`Property "${r}" triggers the "${e}" rule error`,code:"rule:"+e})}))),f)}function f(e={},t,r){if(e.type){const n=!!s(e.type)&&e.type,c=!!o(e.type)&&e.type;if(n||c){const s=n||l.create(t.normalize("NFD").replace(/[\u0300-\u036f]/g,"").match(/[a-z1-9]+/gi).map((e=>e.charAt(0).toUpperCase()+e.substr(1).toLowerCase())).join(""),c,r);return e.type=s,s}}return!1}p.throw=function(e,t,r,n,s){const c=t.split(":")[0];if(s=s&&s.constructor.name,!0===e.exceptions||o(e.exceptions)&&e.exceptions[c])throw new p({message:`[${s}] ${r}`,source:s,property:n,code:t})};class l extends class{constructor(n,o){return new Proxy(this,{set(i,u,l){const y=n[u]||{},$=i[u],h=t($),d=!(h||(g=l,m=$,JSON.stringify(g)===JSON.stringify(m)));var g,m;const b=f(y,u,o);function S(e,r){const s=Reflect.apply(e,i,r||[u,l,$,n]);return t(s)?l:s}var w;l=S(i.$onBeforeSet),d&&(l=S(i.$onBeforeUpdate)),r(y.transform)&&(l=S(y.transform,[l,n])),e(w=y.type)&&1===w.length&&s(w[0])&&!t(l)&&(l=y.type[0].$hydrate(l),y.type=Array);const j=a(y,u,l,h,o);if(j.length){if(!o.exceptions)return!0;p.throw(o,j[0].code,j[0].message,u,i)}return o.strict&&!c(y).length||(b&&!t(l)&&(l=new b(l)),i[u]=l,S(i.$onSet),d&&(S(i.$onUpdate),i.$applySubscribers(u,l))),!0},get(e,o){const i=n[o];let u=e[o];if(["$get"].includes(o))return function(){return function(e){return c(e=Object.assign({},e),(t=>{s(e[t])&&(e[t]=e[t].$get())})),e}(e)};if(!i)return e[o];function p(r,s){const c=Reflect.apply(r,e,s||[o,u,n]);return t(c)?u:c}return u=p(e.$onBeforeGet),r(i)&&(u=p(i,[e,n])),r(i.format)&&(u=p(i.format,[u,n])),u=p(e.$onGet),u},deleteProperty(e,t){const r=e[t];function s(s,o){return Reflect.apply(s,e,o||[t,r,n])}return(n[t]||{}).required&&p.throw(o,"required",`Property "${t}" is "required"`,t,e),!1===s(e.$onBeforeDelete)||(Reflect.deleteProperty(e,t),s(e.$onDelete),s(e.$onUpdate),e.$applySubscribers(t,r)),!0}})}}{constructor(e={},t={},r){super(e,r),this.$post(t)}$patch(e){c(e,(t=>{this[t]=e[t]}))}$post(e){const r=this.$schema();c(r,((r,n)=>{t(e[r])&&(this[r]=t(n.default)?void 0:n.default)})),c(this,((e,n)=>{t(r[e]&&t(n))&&this.$delete(e)})),this.$patch(e)}$put(e){return this.$post(e)}$delete(e){i(e).forEach((e=>{Reflect.deleteProperty(this,e)}))}$subscribe(e){return this.$subscribers().push(e),()=>{this.$subscribers(e)}}}return l.settings={empty:e=>""===e||null===e||t(e),strict:!1,exceptions:{readonly:!1,required:!0,rule:!0,strict:!1,type:!0},methods:{$onBeforeGet:()=>{},$onBeforeSet:()=>{},$onBeforeUpdate:()=>{},$onDelete:()=>{},$onGet:()=>{},$onBeforeDelete:()=>{},$onSet:()=>{},$onUpdate:()=>{}}},l.create=function(t,r,n){let s=[];n=u(l.settings,n);const o={[t]:class extends l{constructor(e){super(r,e,n)}$schema(){return r}$subscribers(e){return e&&(s=s.filter((t=>t!==e))),s}$applySubscribers(e,t){c(s,((r,n)=>{Reflect.apply(n,this,[e,t,this])}))}}}[t];return o.$check=function(e={},t){const s={};return c(r,((r,o)=>{let c;const u=e[r],p=f(o,r,n);p&&(c=p.$check(u,t));let l=a(o,r,u,!1,n);c?s[r]=c:l.length&&(t&&(l=l.filter((e=>!i(t).includes(e.code)))),l.length&&(s[r]=l))})),!!c(s).length&&s},o.$hydrate=function(t){return e(t)?t.map((e=>new o(e))):new o(t)},Object.assign(o.prototype,n.methods),o},l}(); |
/**! | ||
* SmartModel | ||
* Javascript object model | ||
* https://github.com/jaysalvat/smart-model | ||
* @version 0.5.0 built 2021-02-25 09:18:46 | ||
* @version 0.6.0 built 2021-02-26 08:03:42 | ||
* @license ISC | ||
@@ -30,3 +29,3 @@ * @author Jay Salvat http://jaysalvat.com | ||
function isSmartModel(value) { | ||
return value.prototype instanceof SmartModel || value instanceof SmartModel; | ||
return value && (value.prototype instanceof SmartModel || value instanceof SmartModel); | ||
} | ||
@@ -40,3 +39,3 @@ function isPlainObject(value) { | ||
function keys(obj, cb = function() {}) { | ||
return Object.keys(obj).map(cb); | ||
return Object.keys(obj).map((key => cb(key, obj[key]))); | ||
} | ||
@@ -142,4 +141,3 @@ function toArray(value) { | ||
if (entry.rule) { | ||
keys(entry.rule, (key => { | ||
const rule = entry.rule[key]; | ||
keys(entry.rule, ((key, rule) => { | ||
if (rule(value)) { | ||
@@ -187,3 +185,3 @@ errors.push({ | ||
} | ||
if (isTypeArrayOfSmartModels(entry.type)) { | ||
if (isTypeArrayOfSmartModels(entry.type) && !isUndef(value)) { | ||
value = entry.type[0].$hydrate(value); | ||
@@ -203,3 +201,3 @@ entry.type = Array; | ||
} | ||
if (Nested) { | ||
if (Nested && !isUndef(value)) { | ||
value = new Nested(value); | ||
@@ -211,2 +209,3 @@ } | ||
trigger(target.$onUpdate); | ||
target.$applySubscribers(property, value); | ||
} | ||
@@ -249,6 +248,9 @@ return true; | ||
} | ||
trigger(target.$onBeforeDelete); | ||
if (trigger(target.$onBeforeDelete) === false) { | ||
return true; | ||
} | ||
Reflect.deleteProperty(target, property); | ||
trigger(target.$onDelete); | ||
trigger(target.$onUpdate); | ||
target.$applySubscribers(property, value); | ||
return true; | ||
@@ -262,12 +264,3 @@ } | ||
super(schema, settings); | ||
keys(schema, (key => { | ||
if (isUndef(data[key])) { | ||
if (!isUndef(schema[key].default)) { | ||
this[key] = schema[key].default; | ||
} else if (!isFn(schema[key])) { | ||
this[key] = data[key]; | ||
} | ||
} | ||
})); | ||
this.$patch(data); | ||
this.$post(data); | ||
} | ||
@@ -279,22 +272,23 @@ $patch(data) { | ||
} | ||
$put(data) { | ||
keys(this, (key => { | ||
if (data[key]) { | ||
if (isSmartModel(this[key])) { | ||
this[key].$put(data[key]); | ||
$post(data) { | ||
let undef; | ||
const schema = this.$schema(); | ||
keys(schema, ((key, value) => { | ||
if (isUndef(data[key])) { | ||
if (!isUndef(value.default)) { | ||
this[key] = value.default; | ||
} else { | ||
this[key] = data[key]; | ||
this[key] = undef; | ||
} | ||
} else { | ||
this.$delete(key); | ||
} | ||
})); | ||
keys(data, (key => { | ||
if (!this[key]) { | ||
this[key] = data[key]; | ||
keys(this, ((key, value) => { | ||
if (isUndef(schema[key] && isUndef(value))) { | ||
this.$delete(key); | ||
} | ||
})); | ||
this.$patch(data); | ||
} | ||
$post(data) { | ||
return this.$put(data); | ||
$put(data) { | ||
return this.$post(data); | ||
} | ||
@@ -306,2 +300,8 @@ $delete(properties) { | ||
} | ||
$subscribe(fn) { | ||
this.$subscribers().push(fn); | ||
return () => { | ||
this.$subscribers(fn); | ||
}; | ||
} | ||
} | ||
@@ -330,2 +330,3 @@ SmartModel.settings = { | ||
SmartModel.create = function(name, schema, settings) { | ||
let subscribers = []; | ||
settings = merge(SmartModel.settings, settings); | ||
@@ -337,10 +338,23 @@ const Model = { | ||
} | ||
$schema() { | ||
return schema; | ||
} | ||
$subscribers(removedFn) { | ||
if (removedFn) { | ||
subscribers = subscribers.filter((fn => fn !== removedFn)); | ||
} | ||
return subscribers; | ||
} | ||
$applySubscribers(property, value) { | ||
keys(subscribers, ((_, subscriber) => { | ||
Reflect.apply(subscriber, this, [ property, value, this ]); | ||
})); | ||
} | ||
} | ||
}[name]; | ||
Model.$check = function(payload, filters) { | ||
Model.$check = function(payload = {}, filters) { | ||
const invalidations = {}; | ||
keys(schema, (property => { | ||
keys(schema, ((property, entry) => { | ||
let subErrors; | ||
const value = payload[property]; | ||
const entry = schema[property]; | ||
const Nested = createNested(entry, property, settings); | ||
@@ -372,3 +386,2 @@ if (Nested) { | ||
Object.assign(Model.prototype, settings.methods); | ||
Model.schema = schema; | ||
return Model; | ||
@@ -375,0 +388,0 @@ }; |
@@ -1,2 +0,2 @@ | ||
/*! SmartModel v0.5.0 */ | ||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).SmartModel=t()}(this,(function(){"use strict";function e(e){return Array.isArray(e)}function t(e){return void 0===e}function r(e){return"function"==typeof e}function n(e){return e&&e.toString().startsWith("class")}function o(e){return e.prototype instanceof l||e instanceof l}function s(e){return e&&"[object Object]"===e.toString()}function i(e,t=function(){}){return Object.keys(e).map(t)}function c(e){return[].concat([],e)}function u(e,t){return t=Object.assign({},e,t),i(e,(r=>{s(e[r])&&s(t[r])&&(t[r]=Object.assign({},e[r],u(e[r],t[r])))})),t}class f extends Error{constructor(e){super(e.message),Object.assign(this,e)}}function p(t,r,u,f,p){const a=[];return p.strict&&!i(t||{}).length&&a.push({message:`Property "${r}" can't be set in strict mode`,code:"strict"}),t.required&&p.empty(u)?(a.push({message:`Property "${r}" is "required"`,code:"required"}),a):t.readonly&&!f?(a.push({message:`Property "${r}" is "readonly"`,code:"readonly"}),a):(void 0===u||(!t.type||!t.required&&p.empty(u)||o(t.type)&&s(u)||c(t.type).some((t=>function(t,r){const o=r&&r.toString().match(/^\s*function (\w+)/),s=(o?o[1]:"object").toLowerCase();if("date"===s&&t instanceof r)return!0;if("array"===s&&e(t))return!0;if("object"===s){if(n(r)&&t instanceof r)return!0;if(!n(r)&&typeof t===s)return!0}else if(typeof t===s)return!0;return!1}(u,t)))||a.push({message:`Property "${r}" has an invalid type "${typeof u}"`,code:"type"}),t.rule&&i(t.rule,(e=>{(0,t.rule[e])(u)&&a.push({message:`Property "${r}" triggers the "${e}" rule error`,code:"rule:"+e})}))),a)}function a(e={},t,r){if(e.type){const n=!!o(e.type)&&e.type,i=!!s(e.type)&&e.type;if(n||i){const o=n||l.create(t.normalize("NFD").replace(/[\u0300-\u036f]/g,"").match(/[a-z1-9]+/gi).map((e=>e.charAt(0).toUpperCase()+e.substr(1).toLowerCase())).join(""),i,r);return e.type=o,o}}return!1}f.throw=function(e,t,r,n,o){const i=t.split(":")[0];if(o=o&&o.constructor.name,!0===e.exceptions||s(e.exceptions)&&e.exceptions[i])throw new f({message:`[${o}] ${r}`,source:o,property:n,code:t})};class l extends class{constructor(n,s){return new Proxy(this,{set(c,u,l){const d=n[u]||{},y=c[u],h=t(y),$=!(h||(g=l,m=y,JSON.stringify(g)===JSON.stringify(m)));var g,m;const b=a(d,u,s);function j(e,r){const o=Reflect.apply(e,c,r||[u,l,y,n]);return t(o)?l:o}var w;l=j(c.$onBeforeSet),$&&(l=j(c.$onBeforeUpdate)),r(d.transform)&&(l=j(d.transform,[l,n])),e(w=d.type)&&1===w.length&&o(w[0])&&(l=d.type[0].$hydrate(l),d.type=Array);const x=p(d,u,l,h,s);if(x.length){if(!s.exceptions)return!0;f.throw(s,x[0].code,x[0].message,u,c)}return s.strict&&!i(d).length||(b&&(l=new b(l)),c[u]=l,j(c.$onSet),$&&j(c.$onUpdate)),!0},get(e,s){const c=n[s];let u=e[s];if(["$get"].includes(s))return function(){return function(e){return i(e=Object.assign({},e),(t=>{o(e[t])&&(e[t]=e[t].$get())})),e}(e)};if(!c)return e[s];function f(r,o){const i=Reflect.apply(r,e,o||[s,u,n]);return t(i)?u:i}return u=f(e.$onBeforeGet),r(c)&&(u=f(c,[e,n])),r(c.format)&&(u=f(c.format,[u,n])),u=f(e.$onGet),u},deleteProperty(e,t){const r=e[t];function o(o,s){return Reflect.apply(o,e,s||[t,r,n])}return(n[t]||{}).required&&f.throw(s,"required",`Property "${t}" is "required"`,t,e),o(e.$onBeforeDelete),Reflect.deleteProperty(e,t),o(e.$onDelete),o(e.$onUpdate),!0}})}}{constructor(e={},n={},o){super(e,o),i(e,(o=>{t(n[o])&&(t(e[o].default)?r(e[o])||(this[o]=n[o]):this[o]=e[o].default)})),this.$patch(n)}$patch(e){i(e,(t=>{this[t]=e[t]}))}$put(e){i(this,(t=>{e[t]?o(this[t])?this[t].$put(e[t]):this[t]=e[t]:this.$delete(t)})),i(e,(t=>{this[t]||(this[t]=e[t])}))}$post(e){return this.$put(e)}$delete(e){c(e).forEach((e=>{Reflect.deleteProperty(this,e)}))}}return l.settings={empty:e=>""===e||null===e||t(e),strict:!1,exceptions:{readonly:!1,required:!0,rule:!0,strict:!1,type:!0},methods:{$onBeforeGet:()=>{},$onBeforeSet:()=>{},$onBeforeUpdate:()=>{},$onDelete:()=>{},$onGet:()=>{},$onBeforeDelete:()=>{},$onSet:()=>{},$onUpdate:()=>{}}},l.create=function(t,r,n){n=u(l.settings,n);const o={[t]:class extends l{constructor(e){super(r,e,n)}}}[t];return o.$check=function(e,t){const o={};return i(r,(s=>{let i;const u=e[s],f=r[s],l=a(f,s,n);l&&(i=l.$check(u,t));let d=p(f,s,u,!1,n);i?o[s]=i:d.length&&(t&&(d=d.filter((e=>!c(t).includes(e.code)))),d.length&&(o[s]=d))})),!!i(o).length&&o},o.$hydrate=function(t){return e(t)?t.map((e=>new o(e))):new o(t)},Object.assign(o.prototype,n.methods),o.schema=r,o},l})); | ||
/*! SmartModel v0.6.0 */ | ||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).SmartModel=t()}(this,(function(){"use strict";function e(e){return Array.isArray(e)}function t(e){return void 0===e}function r(e){return"function"==typeof e}function n(e){return e&&e.toString().startsWith("class")}function o(e){return e&&(e.prototype instanceof l||e instanceof l)}function s(e){return e&&"[object Object]"===e.toString()}function i(e,t=function(){}){return Object.keys(e).map((r=>t(r,e[r])))}function c(e){return[].concat([],e)}function u(e,t){return t=Object.assign({},e,t),i(e,(r=>{s(e[r])&&s(t[r])&&(t[r]=Object.assign({},e[r],u(e[r],t[r])))})),t}class p extends Error{constructor(e){super(e.message),Object.assign(this,e)}}function a(t,r,u,p,a){const f=[];return a.strict&&!i(t||{}).length&&f.push({message:`Property "${r}" can't be set in strict mode`,code:"strict"}),t.required&&a.empty(u)?(f.push({message:`Property "${r}" is "required"`,code:"required"}),f):t.readonly&&!p?(f.push({message:`Property "${r}" is "readonly"`,code:"readonly"}),f):(void 0===u||(!t.type||!t.required&&a.empty(u)||o(t.type)&&s(u)||c(t.type).some((t=>function(t,r){const o=r&&r.toString().match(/^\s*function (\w+)/),s=(o?o[1]:"object").toLowerCase();if("date"===s&&t instanceof r)return!0;if("array"===s&&e(t))return!0;if("object"===s){if(n(r)&&t instanceof r)return!0;if(!n(r)&&typeof t===s)return!0}else if(typeof t===s)return!0;return!1}(u,t)))||f.push({message:`Property "${r}" has an invalid type "${typeof u}"`,code:"type"}),t.rule&&i(t.rule,((e,t)=>{t(u)&&f.push({message:`Property "${r}" triggers the "${e}" rule error`,code:"rule:"+e})}))),f)}function f(e={},t,r){if(e.type){const n=!!o(e.type)&&e.type,i=!!s(e.type)&&e.type;if(n||i){const o=n||l.create(t.normalize("NFD").replace(/[\u0300-\u036f]/g,"").match(/[a-z1-9]+/gi).map((e=>e.charAt(0).toUpperCase()+e.substr(1).toLowerCase())).join(""),i,r);return e.type=o,o}}return!1}p.throw=function(e,t,r,n,o){const i=t.split(":")[0];if(o=o&&o.constructor.name,!0===e.exceptions||s(e.exceptions)&&e.exceptions[i])throw new p({message:`[${o}] ${r}`,source:o,property:n,code:t})};class l extends class{constructor(n,s){return new Proxy(this,{set(c,u,l){const y=n[u]||{},d=c[u],h=t(d),$=!(h||(g=l,m=d,JSON.stringify(g)===JSON.stringify(m)));var g,m;const b=f(y,u,s);function S(e,r){const o=Reflect.apply(e,c,r||[u,l,d,n]);return t(o)?l:o}var j;l=S(c.$onBeforeSet),$&&(l=S(c.$onBeforeUpdate)),r(y.transform)&&(l=S(y.transform,[l,n])),e(j=y.type)&&1===j.length&&o(j[0])&&!t(l)&&(l=y.type[0].$hydrate(l),y.type=Array);const w=a(y,u,l,h,s);if(w.length){if(!s.exceptions)return!0;p.throw(s,w[0].code,w[0].message,u,c)}return s.strict&&!i(y).length||(b&&!t(l)&&(l=new b(l)),c[u]=l,S(c.$onSet),$&&(S(c.$onUpdate),c.$applySubscribers(u,l))),!0},get(e,s){const c=n[s];let u=e[s];if(["$get"].includes(s))return function(){return function(e){return i(e=Object.assign({},e),(t=>{o(e[t])&&(e[t]=e[t].$get())})),e}(e)};if(!c)return e[s];function p(r,o){const i=Reflect.apply(r,e,o||[s,u,n]);return t(i)?u:i}return u=p(e.$onBeforeGet),r(c)&&(u=p(c,[e,n])),r(c.format)&&(u=p(c.format,[u,n])),u=p(e.$onGet),u},deleteProperty(e,t){const r=e[t];function o(o,s){return Reflect.apply(o,e,s||[t,r,n])}return(n[t]||{}).required&&p.throw(s,"required",`Property "${t}" is "required"`,t,e),!1===o(e.$onBeforeDelete)||(Reflect.deleteProperty(e,t),o(e.$onDelete),o(e.$onUpdate),e.$applySubscribers(t,r)),!0}})}}{constructor(e={},t={},r){super(e,r),this.$post(t)}$patch(e){i(e,(t=>{this[t]=e[t]}))}$post(e){const r=this.$schema();i(r,((r,n)=>{t(e[r])&&(this[r]=t(n.default)?void 0:n.default)})),i(this,((e,n)=>{t(r[e]&&t(n))&&this.$delete(e)})),this.$patch(e)}$put(e){return this.$post(e)}$delete(e){c(e).forEach((e=>{Reflect.deleteProperty(this,e)}))}$subscribe(e){return this.$subscribers().push(e),()=>{this.$subscribers(e)}}}return l.settings={empty:e=>""===e||null===e||t(e),strict:!1,exceptions:{readonly:!1,required:!0,rule:!0,strict:!1,type:!0},methods:{$onBeforeGet:()=>{},$onBeforeSet:()=>{},$onBeforeUpdate:()=>{},$onDelete:()=>{},$onGet:()=>{},$onBeforeDelete:()=>{},$onSet:()=>{},$onUpdate:()=>{}}},l.create=function(t,r,n){let o=[];n=u(l.settings,n);const s={[t]:class extends l{constructor(e){super(r,e,n)}$schema(){return r}$subscribers(e){return e&&(o=o.filter((t=>t!==e))),o}$applySubscribers(e,t){i(o,((r,n)=>{Reflect.apply(n,this,[e,t,this])}))}}}[t];return s.$check=function(e={},t){const o={};return i(r,((r,s)=>{let i;const u=e[r],p=f(s,r,n);p&&(i=p.$check(u,t));let l=a(s,r,u,!1,n);i?o[r]=i:l.length&&(t&&(l=l.filter((e=>!c(t).includes(e.code)))),l.length&&(o[r]=l))})),!!i(o).length&&o},s.$hydrate=function(t){return e(t)?t.map((e=>new s(e))):new s(t)},Object.assign(s.prototype,n.methods),s},l})); |
{ | ||
"name": "@jaysalvat/smart-model", | ||
"version": "0.5.0", | ||
"description": "Javascript object model", | ||
"version": "0.6.0", | ||
"description": "SmartModel is a fun experiment over Javascript Proxy. It tends to bring useful tools and best practices to data objects.", | ||
"main": "./build/smart-model.cjs.min.cjs", | ||
@@ -6,0 +6,0 @@ "module": "./build/smart-model.esm.min.js", |
@@ -14,17 +14,19 @@ ``` | ||
Javascript object model. | ||
SmartModel is a fun experiment over Javascript Proxy. It tends to bring useful tools and best practices to data objects. | ||
- [x] ~1.5Kb gzipped | ||
- [x] Small footprint, <2Kb gzipped | ||
- [x] Value transformation | ||
- [x] Value format | ||
- [x] Value formatting | ||
- [x] Value type validation | ||
- [x] Value content validation (required, custom rules) | ||
- [x] Value content validation | ||
- [x] Default value | ||
- [x] Readonly properties | ||
- [x] Computed property | ||
- [x] Throw exception (or not) if invalid | ||
- [x] Computed properties | ||
- [x] Exceptions (or not) if invalid | ||
- [x] Nested models | ||
- [x] Live cycle events | ||
- [ ] Better documentation ^^ | ||
- [x] Live cycle callbacks | ||
- [x] Subscriptions | ||
**Important note** SmartModel uses javascript proxy. It unfortunately makes it incompatible with VueJs reactive properties. | ||
Works on modern browsers. | ||
@@ -204,3 +206,3 @@ [Check if tests pass](https://unpkg.com/@jaysalvat/smart-model@latest/test/index.html) on your browser. | ||
```javascript | ||
const cloned = Object.assign({}, model) | ||
const clonedObject = Object.assign({}, model) | ||
``` | ||
@@ -212,9 +214,19 @@ | ||
```javascript | ||
const cloned = model.$get() | ||
const clonedObject = model.$get() | ||
``` | ||
it could also cause unexpected effects when model is used in other Proxies (like Vue). | ||
The formatting could be applied multiple times. | ||
Consider using a computed property instead. | ||
Or consider using a computed property. | ||
```javascript | ||
const Event = SmartModel.create('Event', { | ||
name: { | ||
type: String | ||
}, | ||
date: { | ||
type Date, | ||
}, | ||
formattedDate: (value) => new Date(value).toLocaleString() | ||
}) | ||
``` | ||
#### readonly | ||
@@ -409,3 +421,3 @@ | ||
### Native methods | ||
### Methods | ||
@@ -451,2 +463,24 @@ #### $get | ||
#### $subscribe() | ||
Adds an update listener. It will be called any time a value is updated. | ||
```javascript | ||
article.$subscribe((property, value) => { | ||
console.log(property, 'has been updated with', value) | ||
}) | ||
``` | ||
To unsubscribe the change listener, invoke the function returned by subscribe. | ||
```javascript | ||
const unsubscribe = article.$subscribe((property, value) => { | ||
console.log(property, 'has been updated with', value) | ||
}) | ||
unsubscribe() | ||
``` | ||
## Static methods | ||
#### $check | ||
@@ -453,0 +487,0 @@ |
@@ -26,3 +26,2 @@ /* eslint-disable camelcase */ | ||
* ${NAME} | ||
* ${pkg.description} | ||
* https://github.com/jaysalvat/${FILENAME} | ||
@@ -29,0 +28,0 @@ * @version ${pkg.version} built ${DATE} |
@@ -47,5 +47,3 @@ import { keys, isSmartModel, toArray, checkType, isPlainObject } from './utils.js' | ||
if (entry.rule) { | ||
keys(entry.rule, (key) => { | ||
const rule = entry.rule[key] | ||
keys(entry.rule, (key, rule) => { | ||
if (rule(value)) { | ||
@@ -52,0 +50,0 @@ errors.push({ |
@@ -5,3 +5,3 @@ | ||
import checkErrors from './checkErrors.js' | ||
import { toArray, keys, merge, isSmartModel, isFn, isArray, isUndef } from './utils.js' | ||
import { toArray, keys, merge, isArray, isUndef } from './utils.js' | ||
@@ -12,13 +12,3 @@ class SmartModel extends SmartModelProxy { | ||
keys(schema, (key) => { | ||
if (isUndef(data[key])) { | ||
if (!isUndef(schema[key].default)) { | ||
this[key] = schema[key].default | ||
} else if (!isFn(schema[key])) { | ||
this[key] = data[key] | ||
} | ||
} | ||
}) | ||
this.$patch(data) | ||
this.$post(data) | ||
} | ||
@@ -34,24 +24,27 @@ | ||
$put(data) { | ||
keys(this, (key) => { | ||
if (data[key]) { | ||
if (isSmartModel(this[key])) { | ||
this[key].$put(data[key]) | ||
$post(data) { | ||
let undef | ||
const schema = this.$schema() | ||
keys(schema, (key, value) => { | ||
if (isUndef(data[key])) { | ||
if (!isUndef(value.default)) { | ||
this[key] = value.default | ||
} else { | ||
this[key] = data[key] | ||
this[key] = undef | ||
} | ||
} else { | ||
this.$delete(key) | ||
} | ||
}) | ||
keys(data, (key) => { | ||
if (!this[key]) { | ||
this[key] = data[key] | ||
keys(this, (key, value) => { | ||
if (isUndef(schema[key] && isUndef(value))) { | ||
this.$delete(key) | ||
} | ||
}) | ||
this.$patch(data) | ||
} | ||
$post(data) { | ||
return this.$put(data) | ||
$put(data) { | ||
return this.$post(data) | ||
} | ||
@@ -64,2 +57,10 @@ | ||
} | ||
$subscribe(fn) { | ||
this.$subscribers().push(fn) | ||
return () => { | ||
this.$subscribers(fn) | ||
} | ||
} | ||
} | ||
@@ -90,2 +91,4 @@ | ||
SmartModel.create = function (name, schema, settings) { | ||
let subscribers = [] | ||
settings = merge(SmartModel.settings, settings) | ||
@@ -97,11 +100,29 @@ | ||
} | ||
} }[name] | ||
Model.$check = function (payload, filters) { | ||
$schema() { | ||
return schema | ||
} | ||
$subscribers(removedFn) { | ||
if (removedFn) { | ||
subscribers = subscribers.filter((fn) => fn !== removedFn) | ||
} | ||
return subscribers | ||
} | ||
$applySubscribers(property, value) { | ||
keys(subscribers, (_, subscriber) => { | ||
Reflect.apply(subscriber, this, [ property, value, this ]) | ||
}) | ||
} | ||
} | ||
}[name] | ||
Model.$check = function (payload = {}, filters) { | ||
const invalidations = {} | ||
keys(schema, (property) => { | ||
keys(schema, (property, entry) => { | ||
let subErrors | ||
const value = payload[property] | ||
const entry = schema[property] | ||
const Nested = createNested(entry, property, settings) | ||
@@ -141,4 +162,2 @@ | ||
Model.schema = schema | ||
return Model | ||
@@ -145,0 +164,0 @@ } |
@@ -9,2 +9,3 @@ import SmartModelError from './SmartModelError.js' | ||
return new Proxy(this, { | ||
set(target, property, value) { | ||
@@ -33,3 +34,3 @@ const entry = schema[property] || {} | ||
if (isTypeArrayOfSmartModels(entry.type)) { | ||
if (isTypeArrayOfSmartModels(entry.type) && !isUndef(value)) { | ||
value = entry.type[0].$hydrate(value) | ||
@@ -53,3 +54,3 @@ entry.type = Array | ||
if (Nested) { | ||
if (Nested && !isUndef(value)) { | ||
value = new Nested(value) | ||
@@ -64,2 +65,4 @@ } | ||
trigger(target.$onUpdate) | ||
target.$applySubscribers(property, value) | ||
} | ||
@@ -117,3 +120,5 @@ | ||
trigger(target.$onBeforeDelete) | ||
if (trigger(target.$onBeforeDelete) === false) { | ||
return true | ||
} | ||
@@ -125,2 +130,4 @@ Reflect.deleteProperty(target, property) | ||
target.$applySubscribers(property, value) | ||
return true | ||
@@ -127,0 +134,0 @@ } |
@@ -26,3 +26,3 @@ /* eslint-disable new-cap */ | ||
export function isSmartModel(value) { | ||
return value.prototype instanceof SmartModel || value instanceof SmartModel | ||
return value && (value.prototype instanceof SmartModel || value instanceof SmartModel) | ||
} | ||
@@ -38,4 +38,4 @@ | ||
export function keys(obj, cb = function () { }) { | ||
return Object.keys(obj).map(cb) | ||
export function keys(obj, cb = function () {}) { | ||
return Object.keys(obj).map((key) => cb(key, obj[key])) | ||
} | ||
@@ -42,0 +42,0 @@ |
@@ -428,2 +428,20 @@ /* eslint-disable prefer-reflect */ | ||
}) | ||
it('should intercept delete', () => { | ||
const Model = SmartModel.create('Model', { | ||
prop: { default: 'string' } | ||
}, { | ||
methods: { | ||
$onBeforeDelete() { | ||
return false | ||
} | ||
} | ||
}) | ||
const model = new Model() | ||
delete model.prop | ||
expect(model.prop).to.be.equal('string') | ||
}) | ||
}) | ||
@@ -435,3 +453,3 @@ | ||
describe('$get', function () { | ||
it('should works', function () { | ||
it('should eject data model as standard JSON', function () { | ||
const Model = SmartModel.create('Model', { | ||
@@ -443,6 +461,13 @@ prop1: { default: 'string1' }, | ||
} | ||
}, | ||
prop3: { | ||
type: { | ||
prop: { default: 'string3' } | ||
} | ||
} | ||
}) | ||
const model = new Model() | ||
const model = new Model({ | ||
prop2: {} | ||
}) | ||
const obj = model.$get() | ||
@@ -453,2 +478,3 @@ | ||
expect(model.prop2).to.be.instanceOf(SmartModel) | ||
expect(model.prop3).to.not.be.instanceOf(SmartModel) | ||
expect(obj).to.not.be.instanceOf(SmartModel) | ||
@@ -469,6 +495,12 @@ expect(obj.prop2).to.not.be.instanceOf(SmartModel) | ||
} | ||
}, | ||
prop4: { | ||
type: String | ||
} | ||
}) | ||
const model = new Model() | ||
const model = new Model({ | ||
prop4: 'string5', | ||
unregistred: 'unregistred' | ||
}) | ||
@@ -485,7 +517,9 @@ model.$post({ | ||
expect(model.prop1).to.be.equal('newString1') | ||
expect(model.prop2).to.be.equal(undef) | ||
expect(model.prop2).to.be.equal('string2') | ||
expect(model.new).to.be.equal('string') | ||
expect(model.prop3.prop1).to.be.equal('newString2') | ||
expect(model.prop3.prop2).to.be.equal(undef) | ||
expect(model.prop3.prop2).to.be.equal('string4') | ||
expect(model.prop3.new).to.be.equal('string') | ||
expect(model.prop4).to.be.equal(undef) | ||
expect(model.unregistred).to.be.equal(undef) | ||
}) | ||
@@ -558,2 +592,60 @@ }) | ||
}) | ||
describe('$subscribe', function () { | ||
it('should call subscribersa', function () { | ||
const properties = [] | ||
const values = [] | ||
const Model = SmartModel.create('Model', { | ||
prop1: { default: 'string' }, | ||
prop2: { default: 'string' }, | ||
prop3: { default: 'string' } | ||
}) | ||
const model = new Model() | ||
model.$subscribe((property) => { | ||
properties.push(property) | ||
}) | ||
model.$subscribe((_, value) => { | ||
values.push(value) | ||
}) | ||
model.prop1 = 'new string1' | ||
model.prop2 = 'new string2' | ||
expect(properties).to.have.all.members([ 'prop1', 'prop2' ]) | ||
expect(values).to.have.all.members([ 'new string1', 'new string2' ]) | ||
}) | ||
it('should remove subscribersa', function () { | ||
const properties = [] | ||
const values = [] | ||
const Model = SmartModel.create('Model', { | ||
prop1: { default: 'string' }, | ||
prop2: { default: 'string' } | ||
}) | ||
const model = new Model() | ||
const unsub1 = model.$subscribe((property) => { | ||
properties.push(property) | ||
}) | ||
const unsub2 = model.$subscribe((_, value) => { | ||
values.push(value) | ||
}) | ||
unsub1() | ||
unsub2() | ||
model.prop1 = 'new string1' | ||
model.prop2 = 'new string2' | ||
expect(properties.length).to.be.equal(0) | ||
expect(values.length).to.be.equal(0) | ||
}) | ||
}) | ||
}) | ||
@@ -872,3 +964,3 @@ | ||
prop1: { default: 'string1' }, | ||
prop2: { type: SubModel } | ||
prop2: { default: {}, type: SubModel } | ||
}) | ||
@@ -890,2 +982,3 @@ | ||
prop2: { | ||
default: {}, | ||
type: { | ||
@@ -909,4 +1002,6 @@ prop: { | ||
prop: { | ||
default: {}, | ||
type: { | ||
prop: { | ||
default: {}, | ||
type: { | ||
@@ -1011,2 +1106,3 @@ nestedNestedProp: { | ||
prop: { | ||
default: {}, | ||
type: { | ||
@@ -1013,0 +1109,0 @@ prop: { |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
128489
3105
590