New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@reactway/forms-core

Package Overview
Dependencies
Maintainers
3
Versions
32
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@reactway/forms-core - npm Package Compare versions

Comparing version 0.0.0-canary.585eb1d to 0.0.0-canary.6baa6d9

rollup.config.js

1

dist/constants.d.ts
export declare const IdSeparator = ".";
export declare const FormSelector: unique symbol;
//# sourceMappingURL=constants.d.ts.map

@@ -25,2 +25,1 @@ import { StoreUpdater } from "../store";

export {};
//# sourceMappingURL=field-helpers.d.ts.map
import { Store } from "../store";
import { Dictionary, PartialKeys } from "./type-helpers";
import { Dictionary } from "./type-helpers";
import { ValidationUpdater, ValueUpdater, StatusUpdater } from "./state-updaters";

@@ -7,5 +7,7 @@ import { ValidationResult, Validator, CancellationToken } from "./validation";

import { Modifier } from "./modifiers";
export interface FieldState<TValue, TData extends {}> extends FieldValue<TValue, FieldState<TValue, TData>> {
export interface FieldStateIdentifiers {
id: string;
name: string;
}
export interface FieldState<TValue, TData extends {}> extends FieldStateIdentifiers, FieldValue<TValue, FieldState<TValue, TData>> {
status: FieldStatus;

@@ -72,3 +74,3 @@ computedValue: boolean;

export declare type DefaultFieldState = Pick<FieldState<any, any>, "fields" | "status" | "validation">;
export declare type Initial<TFieldState extends FieldState<any, any>> = Omit<PartialKeys<TFieldState, keyof DefaultFieldState>, "id" | "name" | "fields">;
export declare type Initial<TFieldState extends FieldState<any, any>> = Pick<TFieldState, "computedValue" | "data" | "getValue" | "setValue">;
export declare type UpdaterId<TUpdater extends Updater> = TUpdater extends Updater<infer TId> ? TId : never;

@@ -78,2 +80,1 @@ export declare type FieldStateValue<TFieldState extends FieldState<any, any>> = TFieldState extends FieldState<infer TValue, any> ? TValue : never;

export declare type RenderValue<TData extends InputFieldData<any, any>> = TData extends InputFieldData<any, infer TRenderValue> ? TRenderValue : never;
//# sourceMappingURL=field-state.d.ts.map

@@ -10,2 +10,1 @@ import { NestedDictionary } from "./type-helpers";

}
//# sourceMappingURL=form-state.d.ts.map

@@ -10,2 +10,1 @@ export * from "./type-helpers";

export * from "./order-guards";
//# sourceMappingURL=index.d.ts.map

@@ -17,2 +17,1 @@ import { DeepReadonly } from "./type-helpers";

}
//# sourceMappingURL=modifiers.d.ts.map
export interface OrderGuard {
reportIndex: (id: string) => void;
}
//# sourceMappingURL=order-guards.d.ts.map

@@ -24,3 +24,4 @@ import { FieldStatus, Updater, TextSelection } from "./field-state";

setFormErrors(errors: NestedDictionary<ValidationResultOrString[]>): void;
setFieldValidationResults(fieldSelector: FieldSelector, errors: ValidationResultOrString[]): void;
resetFieldValidationResults(fieldSelector: FieldSelector): void;
}
//# sourceMappingURL=state-updaters.d.ts.map

@@ -21,2 +21,1 @@ import { FieldState, Initial, Updater, FieldStatus, UpdatersFactories, UpdaterId } from "./field-state";

}
//# sourceMappingURL=store-helpers.d.ts.map

@@ -19,2 +19,2 @@ export interface Dictionary<TValue> {

};
//# sourceMappingURL=type-helpers.d.ts.map
export declare type OmitStrict<T, TKeys extends keyof T> = Omit<T, TKeys>;

@@ -37,2 +37,1 @@ export interface Validator<TValue> {

}
//# sourceMappingURL=validation.d.ts.map

@@ -11,2 +11,1 @@ import { CancellationToken } from "../contracts";

}
//# sourceMappingURL=cancellation-token.d.ts.map

@@ -7,2 +7,1 @@ import { FieldHelpers, ValidatorsFieldHelpers, ModifiersFieldHelpers, InputFieldHelpers, FieldSelector } from "../contracts";

export {};
//# sourceMappingURL=field-helpers.d.ts.map

@@ -18,2 +18,1 @@ import { TinyEmitter } from "@reactway/tiny-emitter";

export {};
//# sourceMappingURL=form-stores-registry.d.ts.map

@@ -14,2 +14,1 @@ import { FieldStatus, InputFieldData, UpdatersFactories, FieldValidation, FieldState, Updater, DefaultFieldState, FieldSelector, InputValues } from "../contracts";

export declare function isInputFieldData(candidate: {}): candidate is InputFieldData<any, any>;
//# sourceMappingURL=generic.d.ts.map

@@ -6,2 +6,1 @@ export * from "./generic";

export * from "./field-helpers";
//# sourceMappingURL=index.d.ts.map

@@ -9,2 +9,1 @@ import { Draft } from "immer";

export declare function selectFieldParent(state: FieldState<any, any>, fieldId: string): FieldState<any, any> | undefined;
//# sourceMappingURL=store-helpers.d.ts.map

@@ -7,2 +7,1 @@ export * from "./contracts";

export * from "./constants";
//# sourceMappingURL=index.d.ts.map

@@ -1,2 +0,27 @@

import{TinyEmitter as t}from"@reactway/tiny-emitter";import e from"immer";import n from"shortid";const i="value",r="status",s="validation";var a,o;!function(t){t[t.Error=0]="Error",t[t.Warning=1]="Warning"}(a||(a={})),function(t){t[t.Unknown=0]="Unknown",t[t.Validation=1]="Validation",t[t.FormSubmit=2]="FormSubmit"}(o||(o={}));const l=".",d=Symbol("form");
import { TinyEmitter } from '@reactway/tiny-emitter';
import produce from 'immer';
import shortid from 'shortid';
import Debug from 'debug';
const ValueUpdater = "value";
const StatusUpdater = "status";
const ValidationUpdater = "validation";
// These comments originated in the v4 codebase and are kept for historical purpose:
// https://github.com/SimplrJS/react-forms/blame/e8443591f215fbd3fa76898520c8490d5c6673b6/packages/simplr-forms/src/contracts/error.ts
var ValidationResultType;
(function (ValidationResultType) {
ValidationResultType[ValidationResultType["Error"] = 0] = "Error";
ValidationResultType[ValidationResultType["Warning"] = 1] = "Warning";
})(ValidationResultType || (ValidationResultType = {}));
var ValidationResultOrigin;
(function (ValidationResultOrigin) {
ValidationResultOrigin[ValidationResultOrigin["Unknown"] = 0] = "Unknown";
ValidationResultOrigin[ValidationResultOrigin["Validation"] = 1] = "Validation";
ValidationResultOrigin[ValidationResultOrigin["FormSubmit"] = 2] = "FormSubmit";
})(ValidationResultOrigin || (ValidationResultOrigin = {}));
const IdSeparator = ".";
const FormSelector = Symbol("form");
/*! *****************************************************************************

@@ -16,2 +41,891 @@ Copyright (c) Microsoft Corporation. All rights reserved.

***************************************************************************** */
function u(t,e,n,i){return new(n||(n=Promise))((function(r,s){function a(t){try{l(i.next(t))}catch(t){s(t)}}function o(t){try{l(i.throw(t))}catch(t){s(t)}}function l(t){t.done?r(t.value):new n((function(e){e(t.value)})).then(a,o)}l((i=i.apply(t,e||[])).next())}))}class c{constructor(t,e){this.tokenName=e,this.cancelled=!1,this.cancellationCallback=t}get cancellationRequested(){return this.cancelled}cancel(){var t;this.cancelled||(this.cancelled=!0,null===(t=this.cancellationCallback)||void 0===t||t.call(this))}}class f extends t{constructor(){super(...arguments),this.storesRegistry={}}registerStore(t,e){if(null!=this.storesRegistry[t])throw new Error(`Form with formId "${t}" is already registered.`);this.storesRegistry[t]=e,this.emit()}unregisterStore(t){this.storesRegistry[t]=void 0,this.emit()}getStore(t){return this.storesRegistry[t]}getStoresIds(){return Object.keys(this.storesRegistry)}}const h=new class{setFormStoresHandler(t,e=!0){e&&null!=this.instance&&delete this.instance,this.instance=t}get registry(){var t;return this.instance=null!==(t=this.instance)&&void 0!==t?t:new f,this.instance}};function g(t,e){return{registerValidator:e=>n=>n.getUpdater("validation").registerValidator(t,e),unregisterValidator:e=>n=>{n.getUpdater("validation").unregisterValidator(t,e)},reportValidatorIndex:e.reportValidatorIndex}}function v(t,e){return Object.assign(Object.assign(Object.assign({},g(t,e)),e),{registerModifier:e=>n=>n.getUpdater("value").registerModifier(t,e),unregisterModifier:e=>n=>{n.getUpdater("value").unregisterModifier(t,e)}})}function p(t,e,i){return{id:"validation",validateField:e=>u(this,void 0,void 0,(function*(){return function(t,e,n,i){return u(this,void 0,void 0,(function*(){const e=t.selectField(i);if(E(e,i),null!=e.validation.currentValidation&&e.validation.currentValidation.cancellationToken.cancel(),0===Object.keys(e.validation.validators).length)return;const r=[];for(const t of e.validation.validatorsOrder){const n=e.validation.validators[t];null!=n&&r.push(Object.assign({},n))}const s=e.getValue(e),a=new Date,l=new c(()=>{n.update(t=>{const e=t.selectField(i);if(null==e)return;const n=e.validation;null!=n.currentValidation&&n.currentValidation.started.getTime()===a.getTime()&&(n.currentValidation=void 0)})});e.validation.currentValidation={started:a,cancellationToken:l},e.validation.results=[];for(const t of r){if(!t.shouldValidate(s))continue;if(l.cancellationRequested)return;const e=t.validate(s,m(o.Validation,t.name));if(null==e)continue;let r=void 0;if(r=O(e)?yield e:e,0===r.length)continue;const a=r.map(e=>y(e,t.name));F(i,n,l,t=>{t.validation.results.push(...a)})}F(i,n,l,t=>{t.validation.currentValidation=void 0})}))}(t,0,i,e)})),registerValidator:(e,i)=>{const r=t.selectField(e);E(r,e);const s=n.generate();return r.validation.validators[s]=Object.assign(Object.assign({},i),{id:s}),r.validation.validatorsOrder.push(s),s},unregisterValidator:(e,n)=>{const i=t.selectField(e);if(E(i,e),null==i.validation.validators[n])return;delete i.validation.validators[n];const r=i.validation.validatorsOrder.findIndex(t=>t===n);-1!==r&&i.validation.validatorsOrder.splice(r,1)},setFormErrors:t=>{!function t(e,n){for(const i of Object.keys(n)){const r=n[i],s=e.fields[i];if(null==s||null==r)continue;if("object"==typeof r&&!Array.isArray(r)){t(s,r);continue}const l=[];for(const t of r){let e;e="string"!=typeof t?t:{message:t,type:a.Error,origin:o.FormSubmit},l.push(e)}s.validation.results=l}}(e,t)}}}function m(t,e){return{error:(n,i)=>({type:a.Error,message:n,code:i,origin:t,validatorName:e}),warning:(n,i)=>({type:a.Warning,message:n,code:i,origin:t,validatorName:e})}}function F(t,e,n,i){n.cancellationRequested||setTimeout(()=>{n.cancellationRequested||e.update(e=>{const n=e.selectField(t);null!=n&&i(n)})},0)}function y(t,e){return"string"!=typeof t?t:{message:t,validatorName:e,type:a.Error,origin:o.Validation}}function V(t){const e={id:"value",updateFieldValue:(e,n,i)=>{var r,s;const a=t.selectField(e);if(E(a,e),!A(a.data))throw new Error("Not implemented.");const o=a.data.modifiers,l=Object.keys(o);if(0===l.length)return a.data.currentValue=n,a.data.selection=i,void t.updateFieldStatus(e,t=>{t.touched=!0,t.pristine=n===a.data.initialValue});const d={value:null!==(r=a.data.transientValue)&&void 0!==r?r:a.data.currentValue,caretPosition:null===(s=a.data.selection)||void 0===s?void 0:s.selectionStart};let u=n,c=void 0,f=null==i?void 0:i.selectionStart;for(const t of l){const e=o[t];if(null==e)throw new Error("Should never happen.");const n={value:u,caretPosition:f},i=e.parse(n,d);u=i.currentValue,null!=i.caretPosition&&(f=i.caretPosition),null==c&&null!=i.transientValue&&(c=i.transientValue)}let h=void 0;null!=f&&(h={selectionStart:f,selectionEnd:f,selectionDirection:"none"}),a.data.currentValue=u,a.data.transientValue=c,console.group("Setting new selection to:"),console.log(Object.assign({},h)),console.groupEnd(),a.data.selection=h},resetFieldValue:n=>{const i=t.selectField(n);if(E(i,n),!A(i.data))throw new Error("Only input field can be reset.");e.updateFieldValue(n,i.data.initialValue)},clearFieldValue:n=>{const i=t.selectField(n);if(E(i,n),!A(i.data))throw new Error("Only input field can be cleared.");e.updateFieldValue(n,i.data.defaultValue)},registerModifier:(e,i)=>{const r=t.selectField(e);if(E(r,e),!A(r.data))throw new Error("Not implemented.");const s=n.generate();return r.data.modifiers[s]=Object.assign(Object.assign({},i),{id:s}),s},unregisterModifier:(e,n)=>{const i=t.selectField(e);if(E(i,e),!A(i.data))throw new Error("Not implemented.");if(null==i.data.modifiers[n])return;const r=i.data.modifiersOrder.findIndex(t=>t===n);-1!==r&&i.data.modifiersOrder.splice(r,1)}};return e}function w(t,n){return{id:"status",updateFieldStatus:(i,r)=>{const s=t.selectField(i);E(s,i);const a=e(s.status,()=>{});let o;const l=e(a,t=>{r(t)},t=>{o=t});r(s.status);const d=t=>null!=o.find(e=>e.path.includes(t));l.touched&&d("touched")&&b(n,t,i,t=>{t.touched=!0}),d("pristine")&&b(n,t,i,t=>{t.pristine=l.pristine}),d("readonly")&&function t(e,n){for(const i of Object.keys(e.fields)){const r=e.fields[i];null!=r&&(n(r.status),t(r,n))}}(s,t=>{t.readonly=l.readonly})}}}function b(t,e,n,i){E(e.selectField(n),n);let r=n;for(;null!=(r=e.getFieldParentId(r));){const t=e.selectField(r);E(t,n),i(t.status)}i(t.status)}function O(t){return null!=t.then&&null!=t.catch}function I(t,e){return null==e?t:`${e}.${t}`}function j(t){const e=t.lastIndexOf(".");return-1===e?t:t.slice(e+".".length)}function E(t,e){if(null==t)throw new Error(`Field '${String(e)}' does not exist in a given state.`)}function S(t,e){if(null==t)throw new Error(`Updater '${e}' does not exist.`)}function k(){return{fields:{},status:{disabled:!1,pristine:!0,touched:!1,readonly:!1,permanent:!1},validation:{results:[],validators:{},validatorsOrder:[]}}}function x(){return{disabled:!1,pristine:!0,touched:!1,readonly:!1,permanent:!1}}function R(t,e,n,i){return void 0===e&&(e=t),void 0===n&&(n=e),{defaultValue:t,initialValue:e,currentValue:n,transientValue:i}}function $(t,e,n,i){return void 0===e&&(e=t),void 0===n&&(n=e),{defaultValue:t,initialValue:e,currentValue:n,transientValue:i,modifiers:{},modifiersOrder:[]}}function U(){return{validation:p,value:V,status:w}}function P(){return{results:[],validators:{},validatorsOrder:[]}}function A(t){const e=t;return void 0!==e.defaultValue&&void 0!==e.initialValue&&void 0!==e.currentValue&&void 0!==e.modifiers&&"object"==typeof e.modifiers&&Array.isArray(e.modifiersOrder)}function B(t,e){const n=n=>{if(n===d)return t;const i=e[n];if(null!=i)return i;const r=N(t,n);return null!=r&&(e[n]=r),r};return{selectField:n,selectFieldParent:t=>{const e=T(t);if(null!=e)return n(e)},getActiveFieldId:()=>t.data.activeFieldId,getFieldParentId:T,getFormValue:()=>t.getValue(t)}}function M(t,e,n,i){const r=B(e,i),s=Object.assign(Object.assign({},r),{registerField:(t,n)=>{!function(t,e,n){if(n.computedValue&&A(n.data))throw new Error(`Field ${e} is marked to have computedValue, but also has data of an input field.`);const i=j(e),r=function t(e,n){const i=n.indexOf(".");if(-1===i)return e;{const r=n.slice(0,i),s=n.slice(i+".".length),a=e.fields[r];if(null==a)return;return t(a,s)}}(t,e);if(null==r)throw new Error(`Parent for field '${e}' to be registered on was not found.`);const s=r.fields[i];if(null!=s&&!1===s.status.permanent)throw new Error(`Field '${e}' has already been registered.`);if(null==s){r.fields[i]=Object.assign(Object.assign(Object.assign({},{fields:{},status:{disabled:!1,pristine:!0,touched:!1,readonly:!1,permanent:!1},validation:{results:[],validators:{},validatorsOrder:[]}}),n),{id:e,name:i,fields:{}})}}(e,t,n)},unregisterField:t=>{!function(t,e){const n=q(t,e);if(null==n)return;const i=j(e),r=n.fields[i];if(null==r)return;if(r.status.permanent)return;n.fields[i]=void 0}(e,t)},setActiveFieldId:t=>{e.data.activeFieldId=t},updateFieldStatus:(t,e)=>{s.getUpdater("status").updateFieldStatus(t,e)},getUpdater:i=>function(t,e,n,i,r){const s=i[r];if(null!=s)return s(t,e,n);return}(s,e,n,t,i),enqueueUpdate:t=>{setTimeout(()=>n.update(t),0)}});return s}function N(t,e){if(e===d)return t;const n=e,i=n.indexOf(".");if(-1===i)return t.fields[n];{const e=n.slice(0,i),r=n.slice(i+".".length),s=t.fields[e];if(null==s)return;return N(s,r)}}function T(t){if(t===d)return;const e=t.lastIndexOf(".");return-1===e?d:t.slice(0,e)}function q(t,e){const n=T(e);if(null!=n)return N(t,n)}let L=0,_=0,W=0;setInterval(()=>{console.log(`Total updates: ${L}, handlers: ${W}, handler calls: ${_}`)},1e3);class C{constructor(n,i){this.emitter=new t,this.handlers={},this.handlerIdsByFieldId={},this.count=0,this.fieldHandlersListener=t=>{const e=Object.keys(this.handlerIdsByFieldId);if(0===e.length)return;const n=[];t.map(t=>{const e=t.path.filter(t=>"fields"!==t).join(".");n.push(e)});const i=[];for(const t of e){if(i.includes(t))continue;n.some(e=>!!e.startsWith(t)&&"."===e.charAt(t.length))&&i.push(t)}const r=i.map(t=>this.handlerIdsByFieldId[t]).flatMap(t=>t);for(const e of r){const n=this.handlers[e];null!=n&&(_++,n(t))}},this.state=e(n(),()=>{}),this.updaters=null!=i?Object.assign({},i):U(),this.emitter.addListener(this.fieldHandlersListener)}get state(){return this._state}set state(t){var e;if(!0===window.debugState){const n=new Error;console.groupCollapsed("State being updated:",Object.assign({},t)),console.log(null===(e=n.stack)||void 0===e?void 0:e.split("\n")[3]),console.groupEnd()}this._state=t,this._helpers=B(this._state,{})}get helpers(){return this._helpers}getState(){return this.state}addListener(t,e){const n=`h${this.count++}`;if(this.handlers[n]=t,W++,null==e||0===e.length)return this.emitter.addListener(e=>{_++,t(e)});const i=[];for(const t of e){let e=this.handlerIdsByFieldId[t];if(null==e&&(e=[],this.handlerIdsByFieldId[t]=e),e.includes(n))continue;e.push(n);const r=()=>{if(null==this.handlerIdsByFieldId[t])return;const e=this.handlerIdsByFieldId[t],i=e.indexOf(n);-1!==i&&e.splice(i,1)};i.push(r)}return()=>{for(const t of i)t()}}update(t){const n=[];L++;const i=e(this.state,e=>{t(M(this.updaters,e,this,{}),e)},t=>{n.push(...t)});this.state!==i&&(this.state=i,this.emitter.emit(n))}}const H="Reactway-Forms:";const D=new class{multiline(...t){const e=[];for(const n of t)e.push(n),e.push("\n");return e.splice(e.length-1,1),e}log(...t){console.log(...this.multiline("Reactway-Forms:",...t))}warn(...t){console.warn(...this.multiline("Reactway-Forms:",...t))}error(...t){console.error(...this.multiline("Reactway-Forms:",...t))}info(...t){console.info(...this.multiline("Reactway-Forms:",...t))}};export{c as CancellationTokenImpl,d as FormSelector,h as FormsStores,l as IdSeparator,H as LOGGER_PREFIX,r as StatusUpdater,w as StatusUpdaterFactory,C as Store,o as ValidationResultOrigin,a as ValidationResultType,s as ValidationUpdater,p as ValidationUpdaterFactory,i as ValueUpdater,V as ValueUpdaterFactory,E as assertFieldIsDefined,S as assertUpdaterIsDefined,g as constructFieldHelpers,v as constructInputFieldHelpers,B as constructStoreHelpers,M as constructUpdateStoreHelpers,m as constructValidatorHelpers,D as formsLogger,I as generateFieldId,k as getDefaultState,x as getDefaultStatuses,U as getDefaultUpdatersFactories,P as getDefaultValidation,j as getFieldNameFromId,T as getFieldParentId,$ as getInitialInputData,R as getInputValues,A as isInputFieldData,O as isPromise,N as selectField,q as selectFieldParent};
function __awaiter(thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
}
class CancellationTokenImpl {
constructor(cancellationCallback, tokenName) {
this.tokenName = tokenName;
this.cancelled = false;
this.cancellationCallback = cancellationCallback;
}
get cancellationRequested() {
return this.cancelled;
}
cancel() {
var _a;
if (this.cancelled) {
return;
}
this.cancelled = true;
(_a = this.cancellationCallback) === null || _a === void 0 ? void 0 : _a.call(this);
}
}
class StoresRegistry extends TinyEmitter {
constructor() {
super(...arguments);
this.storesRegistry = {};
}
registerStore(formId, store) {
if (this.storesRegistry[formId] != null) {
throw new Error(`Form with formId "${formId}" is already registered.`);
}
this.storesRegistry[formId] = store;
this.emit();
}
unregisterStore(formId) {
this.storesRegistry[formId] = undefined;
this.emit();
}
getStore(formId) {
return this.storesRegistry[formId];
}
getStoresIds() {
return Object.keys(this.storesRegistry);
}
}
class FormsStoresClass {
setFormStoresHandler(newHandler, disposeOldOne = true) {
if (disposeOldOne) {
if (this.instance != null) {
delete this.instance;
}
}
this.instance = newHandler;
}
get registry() {
var _a;
this.instance = (_a = this.instance) !== null && _a !== void 0 ? _a : new StoresRegistry();
return this.instance;
}
}
const FormsStores = new FormsStoresClass();
function constructFieldHelpers(fieldSelector, orderGuards) {
return {
registerValidator: validator => registerHelpers => {
const validationUpdater = registerHelpers.getUpdater("validation");
return validationUpdater.registerValidator(fieldSelector, validator);
},
unregisterValidator: validator => unregisterHelpers => {
const validationUpdater = unregisterHelpers.getUpdater("validation");
validationUpdater.unregisterValidator(fieldSelector, validator);
},
reportValidatorIndex: orderGuards.reportValidatorIndex
};
}
function constructInputFieldHelpers(fieldId, orderGuards) {
return Object.assign(Object.assign(Object.assign({}, constructFieldHelpers(fieldId, orderGuards)), orderGuards), { registerModifier: modifier => registerHelpers => {
const valueUpdater = registerHelpers.getUpdater("value");
return valueUpdater.registerModifier(fieldId, modifier);
}, unregisterModifier: modifierId => unregisterHelpers => {
const valueUpdater = unregisterHelpers.getUpdater("value");
valueUpdater.unregisterModifier(fieldId, modifierId);
} });
}
function ValidationUpdaterFactory(helpers, state, store) {
return {
id: "validation",
validateField: (fieldSelector) => __awaiter(this, void 0, void 0, function* () {
return validateField(helpers, state, store, fieldSelector);
}),
registerValidator: (fieldSelector, validator) => {
const fieldState = helpers.selectField(fieldSelector);
assertFieldIsDefined(fieldState, fieldSelector);
const id = shortid.generate();
const mutableValidators = fieldState.validation.validators;
mutableValidators[id] = Object.assign(Object.assign({}, validator), { id: id });
const mutableValidatorsOrder = fieldState.validation.validatorsOrder;
mutableValidatorsOrder.push(id);
return id;
},
unregisterValidator: (fieldId, validatorId) => {
const fieldState = helpers.selectField(fieldId);
if (fieldState == null || fieldState.validation.validators[validatorId] == null) {
// Gracefully return if fieldState or validator is not found, no need to throw.
return;
}
const mutableValidators = fieldState.validation.validators;
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete mutableValidators[validatorId];
const validatorOrderIndex = fieldState.validation.validatorsOrder.findIndex(x => x === validatorId);
if (validatorOrderIndex === -1) {
return;
}
const mutableValidatorsOrder = fieldState.validation.validatorsOrder;
mutableValidatorsOrder.splice(validatorOrderIndex, 1);
},
setFormErrors: errors => {
// TODO: Rename this updater.
setFormErrors(state, errors);
},
setFieldValidationResults: (fieldSelector, errors) => {
setFieldValidationResults(helpers, fieldSelector, errors);
},
resetFieldValidationResults: fieldSelector => {
resetFieldValidationResults(helpers, fieldSelector);
}
};
}
function validateField(helpers, _draft, store, fieldSelector) {
return __awaiter(this, void 0, void 0, function* () {
const fieldState = helpers.selectField(fieldSelector);
assertFieldIsDefined(fieldState, fieldSelector);
if (fieldState.validation.currentValidation != null) {
fieldState.validation.currentValidation.cancellationToken.cancel();
}
const validatorsKeys = Object.keys(fieldState.validation.validators);
if (validatorsKeys.length === 0) {
return;
}
// Copy validators because we're in an asynchronous context
// and their proxy into current state might be revoked by immer.
const validators = [];
for (const validatorId of fieldState.validation.validatorsOrder) {
const validator = fieldState.validation.validators[validatorId];
if (validator == null) {
// TODO: Should we throw if the validator is not found?
continue;
}
validators.push(Object.assign({}, validator));
}
const fieldValue = fieldState.getValue(fieldState);
// Indicate that the validation has started.
const validationStarted = new Date();
const cancellationToken = new CancellationTokenImpl(() => {
store.update(asyncHelpers => {
const asyncFieldState = asyncHelpers.selectField(fieldSelector);
if (asyncFieldState == null) {
return;
}
const validation = asyncFieldState.validation;
if (validation.currentValidation == null) {
return;
}
// Start times are different, thus a new validation is already in progress.
if (validation.currentValidation.started.getTime() !== validationStarted.getTime()) {
return;
}
validation.currentValidation = undefined;
});
});
// Set current validation and reset results synchronously
// for other validations to not clutter the results and find the cancellation token.
fieldState.validation.currentValidation = {
started: validationStarted,
cancellationToken: cancellationToken
};
fieldState.validation.results = [];
for (const validator of validators) {
if (!validator.shouldValidate(fieldValue)) {
continue;
}
// Check for cancellation before executing possibly costy validate function.
if (cancellationToken.cancellationRequested) {
return;
}
const validatorResult = validator.validate(fieldValue, constructValidatorHelpers(ValidationResultOrigin.Validation, validator.name));
if (validatorResult == null) {
continue;
}
// Gather results.
let results = undefined;
if (!isPromise(validatorResult)) {
results = validatorResult;
}
else {
results = yield validatorResult;
}
if (results.length === 0) {
continue;
}
// Construct proper ValidationResults in case any strings are in results.
const validationResults = results.map(x => resolveValidationResult(x, validator.name));
updateFieldAsync(fieldSelector, store, cancellationToken, state => {
const mutableValidationResults = state.validation.results;
mutableValidationResults.push(...validationResults);
});
}
updateFieldAsync(fieldSelector, store, cancellationToken, state => {
state.validation.currentValidation = undefined;
});
});
}
function constructValidatorHelpers(origin, validatorName) {
return {
error: (message, code) => ({
type: ValidationResultType.Error,
message: message,
code: code,
origin: origin,
validatorName: validatorName
}),
warning: (message, code) => ({
type: ValidationResultType.Warning,
message: message,
code: code,
origin: origin,
validatorName: validatorName
})
};
}
function updateFieldAsync(fieldSelector, store, cancellationToken, updater) {
// Check for cancellation right away.
if (cancellationToken.cancellationRequested) {
return;
}
setTimeout(() => {
// Check for cancellation again at the start of asynchronous scope.
if (cancellationToken.cancellationRequested) {
return;
}
store.update(helpers => {
const fieldState = helpers.selectField(fieldSelector);
if (fieldState == null) {
return;
}
updater(fieldState);
});
}, 0);
}
function resolveValidationResult(value, validatorName) {
if (typeof value !== "string") {
return value;
}
return {
message: value,
validatorName: validatorName,
type: ValidationResultType.Error,
origin: ValidationResultOrigin.Validation
};
}
function setFormErrors(state, errors) {
for (const key of Object.keys(errors)) {
const fieldError = errors[key];
const field = state.fields[key];
if (field == null || fieldError == null) {
continue;
}
if (typeof fieldError === "object" && !Array.isArray(fieldError)) {
setFormErrors(field, fieldError);
continue;
}
const validationResults = [];
for (const error of fieldError) {
let validationResult;
if (typeof error !== "string") {
validationResult = error;
}
else {
validationResult = {
message: error,
type: ValidationResultType.Error,
origin: ValidationResultOrigin.FormSubmit
};
}
validationResults.push(validationResult);
}
field.validation.results = validationResults;
}
}
function setFieldValidationResults(helpers, fieldSelector, errors) {
const fieldState = helpers.selectField(fieldSelector);
assertFieldIsDefined(fieldState);
fieldState.validation.results = errors.map(result => typeof result !== "string"
? result
: {
message: result,
type: ValidationResultType.Error
});
}
function resetFieldValidationResults(helpers, fieldSelector) {
const fieldState = helpers.selectField(fieldSelector);
assertFieldIsDefined(fieldState);
fieldState.validation.results = [];
}
const formsLogger = Debug("@reactway:forms");
const logger = formsLogger.extend("value-updater");
function ValueUpdaterFactory(helpers) {
const valueUpdater = {
id: "value",
updateFieldValue: (fieldId, value, selection) => {
var _a, _b;
const fieldState = helpers.selectField(fieldId);
assertFieldIsDefined(fieldState, fieldId);
if (!isInputFieldData(fieldState.data)) {
// TODO: Review
throw new Error("Not implemented.");
}
fieldState.validation.results = [];
const modifiers = fieldState.data.modifiers;
const modifiersKeys = Object.keys(modifiers);
if (modifiersKeys.length === 0) {
// No modifiers found, thus a value is set directly to currentValue.
fieldState.data.currentValue = value;
fieldState.data.selection = selection;
helpers.updateFieldStatus(fieldId, status => {
status.touched = true;
status.pristine = value === fieldState.data.initialValue;
});
return;
}
// Modifiers found, firing up the modifiers mechanism.
const previousParseValue = {
value: (_a = fieldState.data.transientValue) !== null && _a !== void 0 ? _a : fieldState.data.currentValue,
caretPosition: (_b = fieldState.data.selection) === null || _b === void 0 ? void 0 : _b.selectionStart
};
let newValue = value;
let transientValue = undefined;
let newCaretPosition = selection === null || selection === void 0 ? void 0 : selection.selectionStart;
for (const modifierKey of modifiersKeys) {
const modifier = modifiers[modifierKey];
if (modifier == null) {
throw new Error("Should never happen.");
}
const newParseValue = {
value: newValue,
caretPosition: newCaretPosition
};
const result = modifier.parse(newParseValue, previousParseValue);
newValue = result.currentValue;
// If modifier returned selection
if (result.caretPosition != null) {
// It becomes our newSelection.
newCaretPosition = result.caretPosition;
}
// If transientValue was is null or was already set before
if (transientValue != null || result.transientValue == null) {
// Do nothing.
continue;
}
// When first transientValue is encountered, it wins until it is resolved.
transientValue = result.transientValue;
}
let newSelection = undefined;
if (newCaretPosition != null) {
newSelection = {
selectionStart: newCaretPosition,
selectionEnd: newCaretPosition,
selectionDirection: "none"
};
}
fieldState.data.currentValue = newValue;
fieldState.data.transientValue = transientValue;
logger("Setting new selection to:", Object.assign({}, newSelection));
fieldState.data.selection = newSelection;
},
resetFieldValue: fieldId => {
const fieldState = helpers.selectField(fieldId);
assertFieldIsDefined(fieldState, fieldId);
if (!isInputFieldData(fieldState.data)) {
throw new Error("Only input field can be reset.");
}
valueUpdater.updateFieldValue(fieldId, fieldState.data.initialValue);
},
clearFieldValue: fieldId => {
const fieldState = helpers.selectField(fieldId);
assertFieldIsDefined(fieldState, fieldId);
if (!isInputFieldData(fieldState.data)) {
throw new Error("Only input field can be cleared.");
}
valueUpdater.updateFieldValue(fieldId, fieldState.data.defaultValue);
},
registerModifier: (fieldId, modifier) => {
const fieldState = helpers.selectField(fieldId);
assertFieldIsDefined(fieldState, fieldId);
if (!isInputFieldData(fieldState.data)) {
throw new Error("Not implemented.");
}
const id = shortid.generate();
const modifiers = fieldState.data.modifiers;
const mutableModifiers = modifiers;
mutableModifiers[id] = Object.assign(Object.assign({}, modifier), { id });
return id;
},
unregisterModifier: (fieldId, modifierId) => {
const fieldState = helpers.selectField(fieldId);
if (fieldState == null) {
return;
}
if (!isInputFieldData(fieldState.data)) {
throw new Error("Not implemented.");
}
const modifiers = fieldState.data.modifiers;
if (modifiers[modifierId] == null) {
// Gracefully return if the modifier is not found, no need to throw.
return;
}
const modifierOrderIndex = fieldState.data.modifiersOrder.findIndex(x => x === modifierId);
if (modifierOrderIndex === -1) {
return;
}
const mutableModifiersOrder = fieldState.data.modifiersOrder;
mutableModifiersOrder.splice(modifierOrderIndex, 1);
}
};
return valueUpdater;
}
function StatusUpdaterFactory(helpers, state) {
return {
id: "status",
updateFieldStatus: (fieldSelector, updater) => {
const fieldState = helpers.selectField(fieldSelector);
assertFieldIsDefined(fieldState, fieldSelector);
// eslint-disable-next-line @typescript-eslint/no-empty-function
const prevStatus = produce(fieldState.status, () => { });
let statusPatches;
const newStatus = produce(prevStatus, status => {
updater(status);
}, patches => {
statusPatches = patches;
});
updater(fieldState.status);
const statusChanged = (statusKey) => {
return statusPatches.find(x => x.path.includes(statusKey)) != null;
};
if (newStatus.touched && statusChanged("touched")) {
updateDependentStatusUpwards(state, helpers, fieldSelector, status => {
status.touched = true;
});
}
if (statusChanged("pristine")) {
updateDependentStatusUpwards(state, helpers, fieldSelector, status => {
status.pristine = newStatus.pristine;
});
}
if (statusChanged("readonly")) {
updateDependentStatusDownwards(fieldState, status => {
status.readonly = newStatus.readonly;
});
}
if (prevStatus === newStatus) {
return;
}
}
};
}
function updateDependentStatusDownwards(fieldState, updater) {
// TODO: Review
for (const key of Object.keys(fieldState.fields)) {
const child = fieldState.fields[key];
if (child == null) {
continue;
}
updater(child.status);
updateDependentStatusDownwards(child, updater);
}
}
function updateDependentStatusUpwards(state, helpers, fieldSelector, updater) {
const fieldState = helpers.selectField(fieldSelector);
assertFieldIsDefined(fieldState, fieldSelector);
// Update all parents (that have id) statuses.
let parentId = fieldSelector;
while ((parentId = helpers.getFieldParentId(parentId)) != null) {
const field = helpers.selectField(parentId);
assertFieldIsDefined(field, fieldSelector);
updater(field.status);
}
// Update form (upper-most) status too.
// TODO: Review if this is needed and/or FormSelector should be used.
updater(state.status);
}
function isPromise(candidate) {
return candidate.then != null && candidate.catch != null;
}
function generateFieldId(name, parentId) {
if (parentId == null) {
return name;
}
return `${parentId}${IdSeparator}${name}`;
}
function getFieldNameFromId(fieldId) {
const lastSeparatorIndex = fieldId.lastIndexOf(IdSeparator);
if (lastSeparatorIndex === -1) {
return fieldId;
}
return fieldId.slice(lastSeparatorIndex + IdSeparator.length);
}
function assertFieldIsDefined(field, fieldSelector) {
if (field == null) {
throw new Error(`Field '${String(fieldSelector)}' does not exist in a given state.`);
}
}
function assertUpdaterIsDefined(updater, updaterId) {
if (updater == null) {
throw new Error(`Updater '${updaterId}' does not exist.`);
}
}
function getDefaultState() {
return {
fields: {},
status: getDefaultStatuses(),
validation: getDefaultValidation()
};
}
function getDefaultStatuses() {
return {
disabled: false,
pristine: true,
touched: false,
readonly: false,
permanent: false
};
}
function getInputValues(defaultValue, initialValue, currentValue, transientValue) {
// Triple equals to `undefined`, because `null` might be a valid value.
if (initialValue === undefined) {
initialValue = defaultValue;
}
// Triple equals to `undefined`, because `null` might be a valid value.
if (currentValue === undefined) {
currentValue = initialValue;
}
return {
defaultValue,
initialValue,
currentValue,
transientValue
};
}
function getInitialInputData(defaultValue, initialValue, currentValue, transientValue) {
// Triple equals to `undefined`, because `null` might be a valid value.
if (initialValue === undefined) {
initialValue = defaultValue;
}
// Triple equals to `undefined`, because `null` might be a valid value.
if (currentValue === undefined) {
currentValue = initialValue;
}
return {
defaultValue,
initialValue,
currentValue,
transientValue,
modifiers: {},
modifiersOrder: []
};
}
function getDefaultUpdatersFactories() {
return {
validation: ValidationUpdaterFactory,
value: ValueUpdaterFactory,
status: StatusUpdaterFactory
};
}
function getDefaultValidation() {
return {
results: [],
validators: {},
validatorsOrder: []
};
}
function isInputFieldData(candidate) {
const inputValues = candidate;
return (inputValues.defaultValue !== undefined &&
inputValues.initialValue !== undefined &&
inputValues.currentValue !== undefined &&
inputValues.modifiers !== undefined &&
typeof inputValues.modifiers === "object" &&
Array.isArray(inputValues.modifiersOrder));
}
function constructStoreHelpers(state, fieldsCache) {
const cachedSelectField = fieldSelector => {
if (fieldSelector === FormSelector) {
return state;
}
const cachedField = fieldsCache[fieldSelector];
if (cachedField != null) {
return cachedField;
}
const selectedField = selectField(state, fieldSelector);
if (selectedField != null) {
fieldsCache[fieldSelector] = selectedField;
}
return selectedField;
};
const helpers = {
selectField: cachedSelectField,
selectFieldParent: fieldSelector => {
const parentId = getFieldParentId(fieldSelector);
if (parentId == null) {
return undefined;
}
return cachedSelectField(parentId);
},
getActiveFieldId: () => {
const formState = state;
return formState.data.activeFieldId;
},
getFieldParentId: getFieldParentId,
getFormValue: () => {
return state.getValue(state);
}
};
return helpers;
}
function constructUpdateStoreHelpers(updaters, draft, store, fieldsCache) {
const fieldStoreHelpers = constructStoreHelpers(draft, fieldsCache);
const updateStoreHelpers = Object.assign(Object.assign({}, fieldStoreHelpers), { registerField: (fieldId, initialFieldState) => {
registerField(draft, fieldId, initialFieldState);
}, unregisterField: id => {
unregisterField(draft, id);
}, setActiveFieldId: fieldId => {
const formState = draft;
formState.data.activeFieldId = fieldId;
}, updateFieldStatus: (fieldSelector, updater) => {
const statusUpdater = updateStoreHelpers.getUpdater("status");
statusUpdater.updateFieldStatus(fieldSelector, updater);
}, getUpdater: updaterId => {
return getUpdater(updateStoreHelpers, draft, store, updaters, updaterId);
}, enqueueUpdate: updater => {
setTimeout(() => store.update(updater), 0);
} });
return updateStoreHelpers;
}
function registerField(state, fieldId, initialFieldState) {
if (initialFieldState.computedValue && isInputFieldData(initialFieldState.data)) {
throw new Error(`Field ${fieldId} is marked to have computedValue, but also has data of an input field.`);
}
const fieldName = getFieldNameFromId(fieldId);
const parentField = selectRegistrationParent(state, fieldId);
if (parentField == null) {
throw new Error(`Parent for field '${fieldId}' to be registered on was not found.`);
}
const fieldState = parentField.fields[fieldName];
if (fieldState != null && fieldState.status.permanent === false) {
throw new Error(`Field '${fieldId}' has already been registered.`);
}
if (fieldState == null) {
// Make fields non-read-only
const mutableFields = parentField.fields;
// Add field into the state.
mutableFields[fieldName] = Object.assign(Object.assign(Object.assign({}, getDefaultState()), initialFieldState), { id: fieldId, name: fieldName, fields: {} });
}
}
function unregisterField(state, id) {
const parentField = selectFieldParent(state, id);
if (parentField == null) {
return;
}
const fieldName = getFieldNameFromId(id);
const field = parentField.fields[fieldName];
if (field == null) {
return;
}
if (field.status.permanent) {
return;
}
// Make fields non-read-only
const mutableFields = parentField.fields;
mutableFields[fieldName] = undefined;
}
function getUpdater(helpers, fieldState, store, updaters, updaterId) {
const factory = updaters[updaterId];
if (factory != null) {
return factory(helpers, fieldState, store);
}
return undefined;
}
function selectRegistrationParent(state, fieldId) {
const separatorIndex = fieldId.indexOf(IdSeparator);
if (separatorIndex === -1) {
return state;
}
else {
const firstChildName = fieldId.slice(0, separatorIndex);
const nextChildId = fieldId.slice(separatorIndex + IdSeparator.length);
// TODO: Review
const child = state.fields[firstChildName];
if (child == null) {
return undefined;
}
return selectRegistrationParent(child, nextChildId);
}
}
// TODO: Do we need recursion here?
function selectField(state, selector) {
if (selector === FormSelector) {
return state;
}
const fieldId = selector;
const firstSeparatorIndex = fieldId.indexOf(IdSeparator);
if (firstSeparatorIndex === -1) {
return state.fields[fieldId];
}
else {
const name = fieldId.slice(0, firstSeparatorIndex);
const nextFieldId = fieldId.slice(firstSeparatorIndex + IdSeparator.length);
const child = state.fields[name];
if (child == null) {
return undefined;
}
return selectField(child, nextFieldId);
}
}
function getFieldParentId(fieldSelector) {
if (fieldSelector === FormSelector) {
return undefined;
}
const lastSeparatorIndex = fieldSelector.lastIndexOf(IdSeparator);
if (lastSeparatorIndex === -1) {
return FormSelector;
}
return fieldSelector.slice(0, lastSeparatorIndex);
}
function selectFieldParent(state, fieldId) {
const parentId = getFieldParentId(fieldId);
if (parentId == null) {
return undefined;
}
return selectField(state, parentId);
}
class Store {
constructor(initialStateFactory, updatersFactories) {
// super();
this.emitter = new TinyEmitter();
this.handlers = {};
this.handlerIdsByFieldId = {};
this.count = 0;
this.fieldHandlersListener = (patches) => {
const fieldsKeys = Object.keys(this.handlerIdsByFieldId);
if (fieldsKeys.length === 0) {
return;
}
// Strongly type the key to catch an error if it changes.
const fieldsKey = "fields";
const paths = [];
patches.map(patch => {
const idPath = patch.path.filter(x => x !== fieldsKey).join(IdSeparator);
paths.push(idPath);
});
const fieldsToCall = [];
for (const fieldKey of fieldsKeys) {
// TODO: Is this check needed?
if (fieldsToCall.includes(fieldKey)) {
continue;
}
const fieldHandlersShouldBeCalled = paths.some(path => {
if (!path.startsWith(fieldKey)) {
return false;
}
return path.charAt(fieldKey.length) === IdSeparator;
});
if (!fieldHandlersShouldBeCalled) {
continue;
}
fieldsToCall.push(fieldKey);
}
const handlerIds = fieldsToCall
.map(fieldId => {
// We've accumulated the ids from this object, thus they exist.
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return this.handlerIdsByFieldId[fieldId];
})
.flatMap(x => x);
for (const handlerId of handlerIds) {
const handler = this.handlers[handlerId];
if (handler == null) {
continue;
}
handler(patches);
}
};
// eslint-disable-next-line @typescript-eslint/no-empty-function
this.state = produce(initialStateFactory(), () => { });
this.updaters = updatersFactories != null ? Object.assign({}, updatersFactories) : getDefaultUpdatersFactories();
this.emitter.addListener(this.fieldHandlersListener);
}
get state() {
return this._state;
}
set state(value) {
var _a;
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
// @ts-ignore
if (window.debugState === true) {
const err = new Error();
/* eslint-disable no-console */
console.groupCollapsed("State being updated:", Object.assign({}, value));
console.log((_a = err.stack) === null || _a === void 0 ? void 0 : _a.split("\n")[3]);
console.groupEnd();
/* eslint-enable no-console */
}
this._state = value;
this._helpers = constructStoreHelpers(this._state, {});
}
get helpers() {
return this._helpers;
}
getState() {
return this.state;
}
addListener(handler, dependentFields) {
const handlerId = `h${this.count++}`;
this.handlers[handlerId] = handler;
if (dependentFields == null || dependentFields.length === 0) {
return this.emitter.addListener(patches => {
handler(patches);
});
}
const unregisterCallbacks = [];
for (const fieldId of dependentFields) {
let handlerIds = this.handlerIdsByFieldId[fieldId];
if (handlerIds == null) {
handlerIds = [];
this.handlerIdsByFieldId[fieldId] = handlerIds;
}
if (handlerIds.includes(handlerId)) {
continue;
}
// Register callback
handlerIds.push(handlerId);
const unregisterCallback = () => {
if (this.handlerIdsByFieldId[fieldId] == null) {
return;
}
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion, no-shadow
const handlerIds = this.handlerIdsByFieldId[fieldId];
const index = handlerIds.indexOf(handlerId);
if (index === -1) {
return;
}
handlerIds.splice(index, 1);
};
unregisterCallbacks.push(unregisterCallback);
}
return () => {
for (const callback of unregisterCallbacks) {
callback();
}
};
}
update(updater) {
const patches = [];
const newState = produce(this.state, draft => {
updater(constructUpdateStoreHelpers(this.updaters, draft, this, {}), draft);
}, updatePatches => {
patches.push(...updatePatches);
});
if (this.state === newState) {
return;
}
this.state = newState;
this.emitter.emit(patches);
}
}
export { CancellationTokenImpl, FormSelector, FormsStores, IdSeparator, StatusUpdater, StatusUpdaterFactory, Store, ValidationResultOrigin, ValidationResultType, ValidationUpdater, ValidationUpdaterFactory, ValueUpdater, ValueUpdaterFactory, assertFieldIsDefined, assertUpdaterIsDefined, constructFieldHelpers, constructInputFieldHelpers, constructStoreHelpers, constructUpdateStoreHelpers, constructValidatorHelpers, formsLogger, generateFieldId, getDefaultState, getDefaultStatuses, getDefaultUpdatersFactories, getDefaultValidation, getFieldNameFromId, getFieldParentId, getInitialInputData, getInputValues, isInputFieldData, isPromise, selectField, selectFieldParent };

@@ -1,2 +0,30 @@

"use strict";function t(t){return t&&"object"==typeof t&&"default"in t?t.default:t}Object.defineProperty(exports,"__esModule",{value:!0});var e=require("@reactway/tiny-emitter"),i=t(require("immer")),n=t(require("shortid"));var r,s;(r=exports.ValidationResultType||(exports.ValidationResultType={}))[r.Error=0]="Error",r[r.Warning=1]="Warning",(s=exports.ValidationResultOrigin||(exports.ValidationResultOrigin={}))[s.Unknown=0]="Unknown",s[s.Validation=1]="Validation",s[s.FormSubmit=2]="FormSubmit";const a=Symbol("form");
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
var tinyEmitter = require('@reactway/tiny-emitter');
var produce = _interopDefault(require('immer'));
var shortid = _interopDefault(require('shortid'));
var Debug = _interopDefault(require('debug'));
const ValueUpdater = "value";
const StatusUpdater = "status";
const ValidationUpdater = "validation";
// These comments originated in the v4 codebase and are kept for historical purpose:
(function (ValidationResultType) {
ValidationResultType[ValidationResultType["Error"] = 0] = "Error";
ValidationResultType[ValidationResultType["Warning"] = 1] = "Warning";
})(exports.ValidationResultType || (exports.ValidationResultType = {}));
(function (ValidationResultOrigin) {
ValidationResultOrigin[ValidationResultOrigin["Unknown"] = 0] = "Unknown";
ValidationResultOrigin[ValidationResultOrigin["Validation"] = 1] = "Validation";
ValidationResultOrigin[ValidationResultOrigin["FormSubmit"] = 2] = "FormSubmit";
})(exports.ValidationResultOrigin || (exports.ValidationResultOrigin = {}));
const IdSeparator = ".";
const FormSelector = Symbol("form");
/*! *****************************************************************************

@@ -16,2 +44,922 @@ Copyright (c) Microsoft Corporation. All rights reserved.

***************************************************************************** */
function o(t,e,i,n){return new(i||(i=Promise))((function(r,s){function a(t){try{l(n.next(t))}catch(t){s(t)}}function o(t){try{l(n.throw(t))}catch(t){s(t)}}function l(t){t.done?r(t.value):new i((function(e){e(t.value)})).then(a,o)}l((n=n.apply(t,e||[])).next())}))}class l{constructor(t,e){this.tokenName=e,this.cancelled=!1,this.cancellationCallback=t}get cancellationRequested(){return this.cancelled}cancel(){var t;this.cancelled||(this.cancelled=!0,null===(t=this.cancellationCallback)||void 0===t||t.call(this))}}class d extends e.TinyEmitter{constructor(){super(...arguments),this.storesRegistry={}}registerStore(t,e){if(null!=this.storesRegistry[t])throw new Error(`Form with formId "${t}" is already registered.`);this.storesRegistry[t]=e,this.emit()}unregisterStore(t){this.storesRegistry[t]=void 0,this.emit()}getStore(t){return this.storesRegistry[t]}getStoresIds(){return Object.keys(this.storesRegistry)}}const u=new class{setFormStoresHandler(t,e=!0){e&&null!=this.instance&&delete this.instance,this.instance=t}get registry(){var t;return this.instance=null!==(t=this.instance)&&void 0!==t?t:new d,this.instance}};function c(t,e){return{registerValidator:e=>i=>i.getUpdater("validation").registerValidator(t,e),unregisterValidator:e=>i=>{i.getUpdater("validation").unregisterValidator(t,e)},reportValidatorIndex:e.reportValidatorIndex}}function f(t,e,i){return{id:"validation",validateField:e=>o(this,void 0,void 0,(function*(){return function(t,e,i,n){return o(this,void 0,void 0,(function*(){const e=t.selectField(n);if(x(e,n),null!=e.validation.currentValidation&&e.validation.currentValidation.cancellationToken.cancel(),0===Object.keys(e.validation.validators).length)return;const r=[];for(const t of e.validation.validatorsOrder){const i=e.validation.validators[t];null!=i&&r.push(Object.assign({},i))}const s=e.getValue(e),a=new Date,o=new l(()=>{i.update(t=>{const e=t.selectField(n);if(null==e)return;const i=e.validation;null!=i.currentValidation&&i.currentValidation.started.getTime()===a.getTime()&&(i.currentValidation=void 0)})});e.validation.currentValidation={started:a,cancellationToken:o},e.validation.results=[];for(const t of r){if(!t.shouldValidate(s))continue;if(o.cancellationRequested)return;const e=t.validate(s,p(exports.ValidationResultOrigin.Validation,t.name));if(null==e)continue;let r=void 0;if(r=V(e)?yield e:e,0===r.length)continue;const a=r.map(e=>g(e,t.name));h(n,i,o,t=>{t.validation.results.push(...a)})}h(n,i,o,t=>{t.validation.currentValidation=void 0})}))}(t,0,i,e)})),registerValidator:(e,i)=>{const r=t.selectField(e);x(r,e);const s=n.generate();return r.validation.validators[s]=Object.assign(Object.assign({},i),{id:s}),r.validation.validatorsOrder.push(s),s},unregisterValidator:(e,i)=>{const n=t.selectField(e);if(x(n,e),null==n.validation.validators[i])return;delete n.validation.validators[i];const r=n.validation.validatorsOrder.findIndex(t=>t===i);-1!==r&&n.validation.validatorsOrder.splice(r,1)},setFormErrors:t=>{!function t(e,i){for(const n of Object.keys(i)){const r=i[n],s=e.fields[n];if(null==s||null==r)continue;if("object"==typeof r&&!Array.isArray(r)){t(s,r);continue}const a=[];for(const t of r){let e;e="string"!=typeof t?t:{message:t,type:exports.ValidationResultType.Error,origin:exports.ValidationResultOrigin.FormSubmit},a.push(e)}s.validation.results=a}}(e,t)}}}function p(t,e){return{error:(i,n)=>({type:exports.ValidationResultType.Error,message:i,code:n,origin:t,validatorName:e}),warning:(i,n)=>({type:exports.ValidationResultType.Warning,message:i,code:n,origin:t,validatorName:e})}}function h(t,e,i,n){i.cancellationRequested||setTimeout(()=>{i.cancellationRequested||e.update(e=>{const i=e.selectField(t);null!=i&&n(i)})},0)}function g(t,e){return"string"!=typeof t?t:{message:t,validatorName:e,type:exports.ValidationResultType.Error,origin:exports.ValidationResultOrigin.Validation}}function v(t){const e={id:"value",updateFieldValue:(e,i,n)=>{var r,s;const a=t.selectField(e);if(x(a,e),!S(a.data))throw new Error("Not implemented.");const o=a.data.modifiers,l=Object.keys(o);if(0===l.length)return a.data.currentValue=i,a.data.selection=n,void t.updateFieldStatus(e,t=>{t.touched=!0,t.pristine=i===a.data.initialValue});const d={value:null!==(r=a.data.transientValue)&&void 0!==r?r:a.data.currentValue,caretPosition:null===(s=a.data.selection)||void 0===s?void 0:s.selectionStart};let u=i,c=void 0,f=null==n?void 0:n.selectionStart;for(const t of l){const e=o[t];if(null==e)throw new Error("Should never happen.");const i={value:u,caretPosition:f},n=e.parse(i,d);u=n.currentValue,null!=n.caretPosition&&(f=n.caretPosition),null==c&&null!=n.transientValue&&(c=n.transientValue)}let p=void 0;null!=f&&(p={selectionStart:f,selectionEnd:f,selectionDirection:"none"}),a.data.currentValue=u,a.data.transientValue=c,console.group("Setting new selection to:"),console.log(Object.assign({},p)),console.groupEnd(),a.data.selection=p},resetFieldValue:i=>{const n=t.selectField(i);if(x(n,i),!S(n.data))throw new Error("Only input field can be reset.");e.updateFieldValue(i,n.data.initialValue)},clearFieldValue:i=>{const n=t.selectField(i);if(x(n,i),!S(n.data))throw new Error("Only input field can be cleared.");e.updateFieldValue(i,n.data.defaultValue)},registerModifier:(e,i)=>{const r=t.selectField(e);if(x(r,e),!S(r.data))throw new Error("Not implemented.");const s=n.generate();return r.data.modifiers[s]=Object.assign(Object.assign({},i),{id:s}),s},unregisterModifier:(e,i)=>{const n=t.selectField(e);if(x(n,e),!S(n.data))throw new Error("Not implemented.");if(null==n.data.modifiers[i])return;const r=n.data.modifiersOrder.findIndex(t=>t===i);-1!==r&&n.data.modifiersOrder.splice(r,1)}};return e}function m(t,e){return{id:"status",updateFieldStatus:(n,r)=>{const s=t.selectField(n);x(s,n);const a=i(s.status,()=>{});let o;const l=i(a,t=>{r(t)},t=>{o=t});r(s.status);const d=t=>null!=o.find(e=>e.path.includes(t));l.touched&&d("touched")&&F(e,t,n,t=>{t.touched=!0}),d("pristine")&&F(e,t,n,t=>{t.pristine=l.pristine}),d("readonly")&&function t(e,i){for(const n of Object.keys(e.fields)){const r=e.fields[n];null!=r&&(i(r.status),t(r,i))}}(s,t=>{t.readonly=l.readonly})}}}function F(t,e,i,n){x(e.selectField(i),i);let r=i;for(;null!=(r=e.getFieldParentId(r));){const t=e.selectField(r);x(t,i),n(t.status)}n(t.status)}function V(t){return null!=t.then&&null!=t.catch}function y(t){const e=t.lastIndexOf(".");return-1===e?t:t.slice(e+".".length)}function x(t,e){if(null==t)throw new Error(`Field '${String(e)}' does not exist in a given state.`)}function w(){return{fields:{},status:{disabled:!1,pristine:!0,touched:!1,readonly:!1,permanent:!1},validation:{results:[],validators:{},validatorsOrder:[]}}}function O(){return{disabled:!1,pristine:!0,touched:!1,readonly:!1,permanent:!1}}function b(){return{validation:f,value:v,status:m}}function I(){return{results:[],validators:{},validatorsOrder:[]}}function S(t){const e=t;return void 0!==e.defaultValue&&void 0!==e.initialValue&&void 0!==e.currentValue&&void 0!==e.modifiers&&"object"==typeof e.modifiers&&Array.isArray(e.modifiersOrder)}function R(t,e){const i=i=>{if(i===a)return t;const n=e[i];if(null!=n)return n;const r=E(t,i);return null!=r&&(e[i]=r),r};return{selectField:i,selectFieldParent:t=>{const e=U(t);if(null!=e)return i(e)},getActiveFieldId:()=>t.data.activeFieldId,getFieldParentId:U,getFormValue:()=>t.getValue(t)}}function j(t,e,i,n){const r=R(e,n),s=Object.assign(Object.assign({},r),{registerField:(t,i)=>{!function(t,e,i){if(i.computedValue&&S(i.data))throw new Error(`Field ${e} is marked to have computedValue, but also has data of an input field.`);const n=y(e),r=function t(e,i){const n=i.indexOf(".");if(-1===n)return e;{const r=i.slice(0,n),s=i.slice(n+".".length),a=e.fields[r];if(null==a)return;return t(a,s)}}(t,e);if(null==r)throw new Error(`Parent for field '${e}' to be registered on was not found.`);const s=r.fields[n];if(null!=s&&!1===s.status.permanent)throw new Error(`Field '${e}' has already been registered.`);if(null==s){r.fields[n]=Object.assign(Object.assign(Object.assign({},{fields:{},status:{disabled:!1,pristine:!0,touched:!1,readonly:!1,permanent:!1},validation:{results:[],validators:{},validatorsOrder:[]}}),i),{id:e,name:n,fields:{}})}}(e,t,i)},unregisterField:t=>{!function(t,e){const i=k(t,e);if(null==i)return;const n=y(e),r=i.fields[n];if(null==r)return;if(r.status.permanent)return;i.fields[n]=void 0}(e,t)},setActiveFieldId:t=>{e.data.activeFieldId=t},updateFieldStatus:(t,e)=>{s.getUpdater("status").updateFieldStatus(t,e)},getUpdater:n=>function(t,e,i,n,r){const s=n[r];if(null!=s)return s(t,e,i);return}(s,e,i,t,n),enqueueUpdate:t=>{setTimeout(()=>i.update(t),0)}});return s}function E(t,e){if(e===a)return t;const i=e,n=i.indexOf(".");if(-1===n)return t.fields[i];{const e=i.slice(0,n),r=i.slice(n+".".length),s=t.fields[e];if(null==s)return;return E(s,r)}}function U(t){if(t===a)return;const e=t.lastIndexOf(".");return-1===e?a:t.slice(0,e)}function k(t,e){const i=U(e);if(null!=i)return E(t,i)}let T=0,P=0,$=0;setInterval(()=>{console.log(`Total updates: ${T}, handlers: ${$}, handler calls: ${P}`)},1e3);const D=new class{multiline(...t){const e=[];for(const i of t)e.push(i),e.push("\n");return e.splice(e.length-1,1),e}log(...t){console.log(...this.multiline("Reactway-Forms:",...t))}warn(...t){console.warn(...this.multiline("Reactway-Forms:",...t))}error(...t){console.error(...this.multiline("Reactway-Forms:",...t))}info(...t){console.info(...this.multiline("Reactway-Forms:",...t))}};exports.CancellationTokenImpl=l,exports.FormSelector=a,exports.FormsStores=u,exports.IdSeparator=".",exports.LOGGER_PREFIX="Reactway-Forms:",exports.StatusUpdater="status",exports.StatusUpdaterFactory=m,exports.Store=class{constructor(t,n){this.emitter=new e.TinyEmitter,this.handlers={},this.handlerIdsByFieldId={},this.count=0,this.fieldHandlersListener=t=>{const e=Object.keys(this.handlerIdsByFieldId);if(0===e.length)return;const i=[];t.map(t=>{const e=t.path.filter(t=>"fields"!==t).join(".");i.push(e)});const n=[];for(const t of e){if(n.includes(t))continue;i.some(e=>!!e.startsWith(t)&&"."===e.charAt(t.length))&&n.push(t)}const r=n.map(t=>this.handlerIdsByFieldId[t]).flatMap(t=>t);for(const e of r){const i=this.handlers[e];null!=i&&(P++,i(t))}},this.state=i(t(),()=>{}),this.updaters=null!=n?Object.assign({},n):b(),this.emitter.addListener(this.fieldHandlersListener)}get state(){return this._state}set state(t){var e;if(!0===window.debugState){const i=new Error;console.groupCollapsed("State being updated:",Object.assign({},t)),console.log(null===(e=i.stack)||void 0===e?void 0:e.split("\n")[3]),console.groupEnd()}this._state=t,this._helpers=R(this._state,{})}get helpers(){return this._helpers}getState(){return this.state}addListener(t,e){const i=`h${this.count++}`;if(this.handlers[i]=t,$++,null==e||0===e.length)return this.emitter.addListener(e=>{P++,t(e)});const n=[];for(const t of e){let e=this.handlerIdsByFieldId[t];if(null==e&&(e=[],this.handlerIdsByFieldId[t]=e),e.includes(i))continue;e.push(i);const r=()=>{if(null==this.handlerIdsByFieldId[t])return;const e=this.handlerIdsByFieldId[t],n=e.indexOf(i);-1!==n&&e.splice(n,1)};n.push(r)}return()=>{for(const t of n)t()}}update(t){const e=[];T++;const n=i(this.state,e=>{t(j(this.updaters,e,this,{}),e)},t=>{e.push(...t)});this.state!==n&&(this.state=n,this.emitter.emit(e))}},exports.ValidationUpdater="validation",exports.ValidationUpdaterFactory=f,exports.ValueUpdater="value",exports.ValueUpdaterFactory=v,exports.assertFieldIsDefined=x,exports.assertUpdaterIsDefined=function(t,e){if(null==t)throw new Error(`Updater '${e}' does not exist.`)},exports.constructFieldHelpers=c,exports.constructInputFieldHelpers=function(t,e){return Object.assign(Object.assign(Object.assign({},c(t,e)),e),{registerModifier:e=>i=>i.getUpdater("value").registerModifier(t,e),unregisterModifier:e=>i=>{i.getUpdater("value").unregisterModifier(t,e)}})},exports.constructStoreHelpers=R,exports.constructUpdateStoreHelpers=j,exports.constructValidatorHelpers=p,exports.formsLogger=D,exports.generateFieldId=function(t,e){return null==e?t:`${e}.${t}`},exports.getDefaultState=w,exports.getDefaultStatuses=O,exports.getDefaultUpdatersFactories=b,exports.getDefaultValidation=I,exports.getFieldNameFromId=y,exports.getFieldParentId=U,exports.getInitialInputData=function(t,e,i,n){return void 0===e&&(e=t),void 0===i&&(i=e),{defaultValue:t,initialValue:e,currentValue:i,transientValue:n,modifiers:{},modifiersOrder:[]}},exports.getInputValues=function(t,e,i,n){return void 0===e&&(e=t),void 0===i&&(i=e),{defaultValue:t,initialValue:e,currentValue:i,transientValue:n}},exports.isInputFieldData=S,exports.isPromise=V,exports.selectField=E,exports.selectFieldParent=k;
function __awaiter(thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
}
class CancellationTokenImpl {
constructor(cancellationCallback, tokenName) {
this.tokenName = tokenName;
this.cancelled = false;
this.cancellationCallback = cancellationCallback;
}
get cancellationRequested() {
return this.cancelled;
}
cancel() {
var _a;
if (this.cancelled) {
return;
}
this.cancelled = true;
(_a = this.cancellationCallback) === null || _a === void 0 ? void 0 : _a.call(this);
}
}
class StoresRegistry extends tinyEmitter.TinyEmitter {
constructor() {
super(...arguments);
this.storesRegistry = {};
}
registerStore(formId, store) {
if (this.storesRegistry[formId] != null) {
throw new Error(`Form with formId "${formId}" is already registered.`);
}
this.storesRegistry[formId] = store;
this.emit();
}
unregisterStore(formId) {
this.storesRegistry[formId] = undefined;
this.emit();
}
getStore(formId) {
return this.storesRegistry[formId];
}
getStoresIds() {
return Object.keys(this.storesRegistry);
}
}
class FormsStoresClass {
setFormStoresHandler(newHandler, disposeOldOne = true) {
if (disposeOldOne) {
if (this.instance != null) {
delete this.instance;
}
}
this.instance = newHandler;
}
get registry() {
var _a;
this.instance = (_a = this.instance) !== null && _a !== void 0 ? _a : new StoresRegistry();
return this.instance;
}
}
const FormsStores = new FormsStoresClass();
function constructFieldHelpers(fieldSelector, orderGuards) {
return {
registerValidator: validator => registerHelpers => {
const validationUpdater = registerHelpers.getUpdater("validation");
return validationUpdater.registerValidator(fieldSelector, validator);
},
unregisterValidator: validator => unregisterHelpers => {
const validationUpdater = unregisterHelpers.getUpdater("validation");
validationUpdater.unregisterValidator(fieldSelector, validator);
},
reportValidatorIndex: orderGuards.reportValidatorIndex
};
}
function constructInputFieldHelpers(fieldId, orderGuards) {
return Object.assign(Object.assign(Object.assign({}, constructFieldHelpers(fieldId, orderGuards)), orderGuards), { registerModifier: modifier => registerHelpers => {
const valueUpdater = registerHelpers.getUpdater("value");
return valueUpdater.registerModifier(fieldId, modifier);
}, unregisterModifier: modifierId => unregisterHelpers => {
const valueUpdater = unregisterHelpers.getUpdater("value");
valueUpdater.unregisterModifier(fieldId, modifierId);
} });
}
function ValidationUpdaterFactory(helpers, state, store) {
return {
id: "validation",
validateField: (fieldSelector) => __awaiter(this, void 0, void 0, function* () {
return validateField(helpers, state, store, fieldSelector);
}),
registerValidator: (fieldSelector, validator) => {
const fieldState = helpers.selectField(fieldSelector);
assertFieldIsDefined(fieldState, fieldSelector);
const id = shortid.generate();
const mutableValidators = fieldState.validation.validators;
mutableValidators[id] = Object.assign(Object.assign({}, validator), { id: id });
const mutableValidatorsOrder = fieldState.validation.validatorsOrder;
mutableValidatorsOrder.push(id);
return id;
},
unregisterValidator: (fieldId, validatorId) => {
const fieldState = helpers.selectField(fieldId);
if (fieldState == null || fieldState.validation.validators[validatorId] == null) {
// Gracefully return if fieldState or validator is not found, no need to throw.
return;
}
const mutableValidators = fieldState.validation.validators;
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete mutableValidators[validatorId];
const validatorOrderIndex = fieldState.validation.validatorsOrder.findIndex(x => x === validatorId);
if (validatorOrderIndex === -1) {
return;
}
const mutableValidatorsOrder = fieldState.validation.validatorsOrder;
mutableValidatorsOrder.splice(validatorOrderIndex, 1);
},
setFormErrors: errors => {
// TODO: Rename this updater.
setFormErrors(state, errors);
},
setFieldValidationResults: (fieldSelector, errors) => {
setFieldValidationResults(helpers, fieldSelector, errors);
},
resetFieldValidationResults: fieldSelector => {
resetFieldValidationResults(helpers, fieldSelector);
}
};
}
function validateField(helpers, _draft, store, fieldSelector) {
return __awaiter(this, void 0, void 0, function* () {
const fieldState = helpers.selectField(fieldSelector);
assertFieldIsDefined(fieldState, fieldSelector);
if (fieldState.validation.currentValidation != null) {
fieldState.validation.currentValidation.cancellationToken.cancel();
}
const validatorsKeys = Object.keys(fieldState.validation.validators);
if (validatorsKeys.length === 0) {
return;
}
// Copy validators because we're in an asynchronous context
// and their proxy into current state might be revoked by immer.
const validators = [];
for (const validatorId of fieldState.validation.validatorsOrder) {
const validator = fieldState.validation.validators[validatorId];
if (validator == null) {
// TODO: Should we throw if the validator is not found?
continue;
}
validators.push(Object.assign({}, validator));
}
const fieldValue = fieldState.getValue(fieldState);
// Indicate that the validation has started.
const validationStarted = new Date();
const cancellationToken = new CancellationTokenImpl(() => {
store.update(asyncHelpers => {
const asyncFieldState = asyncHelpers.selectField(fieldSelector);
if (asyncFieldState == null) {
return;
}
const validation = asyncFieldState.validation;
if (validation.currentValidation == null) {
return;
}
// Start times are different, thus a new validation is already in progress.
if (validation.currentValidation.started.getTime() !== validationStarted.getTime()) {
return;
}
validation.currentValidation = undefined;
});
});
// Set current validation and reset results synchronously
// for other validations to not clutter the results and find the cancellation token.
fieldState.validation.currentValidation = {
started: validationStarted,
cancellationToken: cancellationToken
};
fieldState.validation.results = [];
for (const validator of validators) {
if (!validator.shouldValidate(fieldValue)) {
continue;
}
// Check for cancellation before executing possibly costy validate function.
if (cancellationToken.cancellationRequested) {
return;
}
const validatorResult = validator.validate(fieldValue, constructValidatorHelpers(exports.ValidationResultOrigin.Validation, validator.name));
if (validatorResult == null) {
continue;
}
// Gather results.
let results = undefined;
if (!isPromise(validatorResult)) {
results = validatorResult;
}
else {
results = yield validatorResult;
}
if (results.length === 0) {
continue;
}
// Construct proper ValidationResults in case any strings are in results.
const validationResults = results.map(x => resolveValidationResult(x, validator.name));
updateFieldAsync(fieldSelector, store, cancellationToken, state => {
const mutableValidationResults = state.validation.results;
mutableValidationResults.push(...validationResults);
});
}
updateFieldAsync(fieldSelector, store, cancellationToken, state => {
state.validation.currentValidation = undefined;
});
});
}
function constructValidatorHelpers(origin, validatorName) {
return {
error: (message, code) => ({
type: exports.ValidationResultType.Error,
message: message,
code: code,
origin: origin,
validatorName: validatorName
}),
warning: (message, code) => ({
type: exports.ValidationResultType.Warning,
message: message,
code: code,
origin: origin,
validatorName: validatorName
})
};
}
function updateFieldAsync(fieldSelector, store, cancellationToken, updater) {
// Check for cancellation right away.
if (cancellationToken.cancellationRequested) {
return;
}
setTimeout(() => {
// Check for cancellation again at the start of asynchronous scope.
if (cancellationToken.cancellationRequested) {
return;
}
store.update(helpers => {
const fieldState = helpers.selectField(fieldSelector);
if (fieldState == null) {
return;
}
updater(fieldState);
});
}, 0);
}
function resolveValidationResult(value, validatorName) {
if (typeof value !== "string") {
return value;
}
return {
message: value,
validatorName: validatorName,
type: exports.ValidationResultType.Error,
origin: exports.ValidationResultOrigin.Validation
};
}
function setFormErrors(state, errors) {
for (const key of Object.keys(errors)) {
const fieldError = errors[key];
const field = state.fields[key];
if (field == null || fieldError == null) {
continue;
}
if (typeof fieldError === "object" && !Array.isArray(fieldError)) {
setFormErrors(field, fieldError);
continue;
}
const validationResults = [];
for (const error of fieldError) {
let validationResult;
if (typeof error !== "string") {
validationResult = error;
}
else {
validationResult = {
message: error,
type: exports.ValidationResultType.Error,
origin: exports.ValidationResultOrigin.FormSubmit
};
}
validationResults.push(validationResult);
}
field.validation.results = validationResults;
}
}
function setFieldValidationResults(helpers, fieldSelector, errors) {
const fieldState = helpers.selectField(fieldSelector);
assertFieldIsDefined(fieldState);
fieldState.validation.results = errors.map(result => typeof result !== "string"
? result
: {
message: result,
type: exports.ValidationResultType.Error
});
}
function resetFieldValidationResults(helpers, fieldSelector) {
const fieldState = helpers.selectField(fieldSelector);
assertFieldIsDefined(fieldState);
fieldState.validation.results = [];
}
const formsLogger = Debug("@reactway:forms");
const logger = formsLogger.extend("value-updater");
function ValueUpdaterFactory(helpers) {
const valueUpdater = {
id: "value",
updateFieldValue: (fieldId, value, selection) => {
var _a, _b;
const fieldState = helpers.selectField(fieldId);
assertFieldIsDefined(fieldState, fieldId);
if (!isInputFieldData(fieldState.data)) {
// TODO: Review
throw new Error("Not implemented.");
}
fieldState.validation.results = [];
const modifiers = fieldState.data.modifiers;
const modifiersKeys = Object.keys(modifiers);
if (modifiersKeys.length === 0) {
// No modifiers found, thus a value is set directly to currentValue.
fieldState.data.currentValue = value;
fieldState.data.selection = selection;
helpers.updateFieldStatus(fieldId, status => {
status.touched = true;
status.pristine = value === fieldState.data.initialValue;
});
return;
}
// Modifiers found, firing up the modifiers mechanism.
const previousParseValue = {
value: (_a = fieldState.data.transientValue) !== null && _a !== void 0 ? _a : fieldState.data.currentValue,
caretPosition: (_b = fieldState.data.selection) === null || _b === void 0 ? void 0 : _b.selectionStart
};
let newValue = value;
let transientValue = undefined;
let newCaretPosition = selection === null || selection === void 0 ? void 0 : selection.selectionStart;
for (const modifierKey of modifiersKeys) {
const modifier = modifiers[modifierKey];
if (modifier == null) {
throw new Error("Should never happen.");
}
const newParseValue = {
value: newValue,
caretPosition: newCaretPosition
};
const result = modifier.parse(newParseValue, previousParseValue);
newValue = result.currentValue;
// If modifier returned selection
if (result.caretPosition != null) {
// It becomes our newSelection.
newCaretPosition = result.caretPosition;
}
// If transientValue was is null or was already set before
if (transientValue != null || result.transientValue == null) {
// Do nothing.
continue;
}
// When first transientValue is encountered, it wins until it is resolved.
transientValue = result.transientValue;
}
let newSelection = undefined;
if (newCaretPosition != null) {
newSelection = {
selectionStart: newCaretPosition,
selectionEnd: newCaretPosition,
selectionDirection: "none"
};
}
fieldState.data.currentValue = newValue;
fieldState.data.transientValue = transientValue;
logger("Setting new selection to:", Object.assign({}, newSelection));
fieldState.data.selection = newSelection;
},
resetFieldValue: fieldId => {
const fieldState = helpers.selectField(fieldId);
assertFieldIsDefined(fieldState, fieldId);
if (!isInputFieldData(fieldState.data)) {
throw new Error("Only input field can be reset.");
}
valueUpdater.updateFieldValue(fieldId, fieldState.data.initialValue);
},
clearFieldValue: fieldId => {
const fieldState = helpers.selectField(fieldId);
assertFieldIsDefined(fieldState, fieldId);
if (!isInputFieldData(fieldState.data)) {
throw new Error("Only input field can be cleared.");
}
valueUpdater.updateFieldValue(fieldId, fieldState.data.defaultValue);
},
registerModifier: (fieldId, modifier) => {
const fieldState = helpers.selectField(fieldId);
assertFieldIsDefined(fieldState, fieldId);
if (!isInputFieldData(fieldState.data)) {
throw new Error("Not implemented.");
}
const id = shortid.generate();
const modifiers = fieldState.data.modifiers;
const mutableModifiers = modifiers;
mutableModifiers[id] = Object.assign(Object.assign({}, modifier), { id });
return id;
},
unregisterModifier: (fieldId, modifierId) => {
const fieldState = helpers.selectField(fieldId);
if (fieldState == null) {
return;
}
if (!isInputFieldData(fieldState.data)) {
throw new Error("Not implemented.");
}
const modifiers = fieldState.data.modifiers;
if (modifiers[modifierId] == null) {
// Gracefully return if the modifier is not found, no need to throw.
return;
}
const modifierOrderIndex = fieldState.data.modifiersOrder.findIndex(x => x === modifierId);
if (modifierOrderIndex === -1) {
return;
}
const mutableModifiersOrder = fieldState.data.modifiersOrder;
mutableModifiersOrder.splice(modifierOrderIndex, 1);
}
};
return valueUpdater;
}
function StatusUpdaterFactory(helpers, state) {
return {
id: "status",
updateFieldStatus: (fieldSelector, updater) => {
const fieldState = helpers.selectField(fieldSelector);
assertFieldIsDefined(fieldState, fieldSelector);
// eslint-disable-next-line @typescript-eslint/no-empty-function
const prevStatus = produce(fieldState.status, () => { });
let statusPatches;
const newStatus = produce(prevStatus, status => {
updater(status);
}, patches => {
statusPatches = patches;
});
updater(fieldState.status);
const statusChanged = (statusKey) => {
return statusPatches.find(x => x.path.includes(statusKey)) != null;
};
if (newStatus.touched && statusChanged("touched")) {
updateDependentStatusUpwards(state, helpers, fieldSelector, status => {
status.touched = true;
});
}
if (statusChanged("pristine")) {
updateDependentStatusUpwards(state, helpers, fieldSelector, status => {
status.pristine = newStatus.pristine;
});
}
if (statusChanged("readonly")) {
updateDependentStatusDownwards(fieldState, status => {
status.readonly = newStatus.readonly;
});
}
if (prevStatus === newStatus) {
return;
}
}
};
}
function updateDependentStatusDownwards(fieldState, updater) {
// TODO: Review
for (const key of Object.keys(fieldState.fields)) {
const child = fieldState.fields[key];
if (child == null) {
continue;
}
updater(child.status);
updateDependentStatusDownwards(child, updater);
}
}
function updateDependentStatusUpwards(state, helpers, fieldSelector, updater) {
const fieldState = helpers.selectField(fieldSelector);
assertFieldIsDefined(fieldState, fieldSelector);
// Update all parents (that have id) statuses.
let parentId = fieldSelector;
while ((parentId = helpers.getFieldParentId(parentId)) != null) {
const field = helpers.selectField(parentId);
assertFieldIsDefined(field, fieldSelector);
updater(field.status);
}
// Update form (upper-most) status too.
// TODO: Review if this is needed and/or FormSelector should be used.
updater(state.status);
}
function isPromise(candidate) {
return candidate.then != null && candidate.catch != null;
}
function generateFieldId(name, parentId) {
if (parentId == null) {
return name;
}
return `${parentId}${IdSeparator}${name}`;
}
function getFieldNameFromId(fieldId) {
const lastSeparatorIndex = fieldId.lastIndexOf(IdSeparator);
if (lastSeparatorIndex === -1) {
return fieldId;
}
return fieldId.slice(lastSeparatorIndex + IdSeparator.length);
}
function assertFieldIsDefined(field, fieldSelector) {
if (field == null) {
throw new Error(`Field '${String(fieldSelector)}' does not exist in a given state.`);
}
}
function assertUpdaterIsDefined(updater, updaterId) {
if (updater == null) {
throw new Error(`Updater '${updaterId}' does not exist.`);
}
}
function getDefaultState() {
return {
fields: {},
status: getDefaultStatuses(),
validation: getDefaultValidation()
};
}
function getDefaultStatuses() {
return {
disabled: false,
pristine: true,
touched: false,
readonly: false,
permanent: false
};
}
function getInputValues(defaultValue, initialValue, currentValue, transientValue) {
// Triple equals to `undefined`, because `null` might be a valid value.
if (initialValue === undefined) {
initialValue = defaultValue;
}
// Triple equals to `undefined`, because `null` might be a valid value.
if (currentValue === undefined) {
currentValue = initialValue;
}
return {
defaultValue,
initialValue,
currentValue,
transientValue
};
}
function getInitialInputData(defaultValue, initialValue, currentValue, transientValue) {
// Triple equals to `undefined`, because `null` might be a valid value.
if (initialValue === undefined) {
initialValue = defaultValue;
}
// Triple equals to `undefined`, because `null` might be a valid value.
if (currentValue === undefined) {
currentValue = initialValue;
}
return {
defaultValue,
initialValue,
currentValue,
transientValue,
modifiers: {},
modifiersOrder: []
};
}
function getDefaultUpdatersFactories() {
return {
validation: ValidationUpdaterFactory,
value: ValueUpdaterFactory,
status: StatusUpdaterFactory
};
}
function getDefaultValidation() {
return {
results: [],
validators: {},
validatorsOrder: []
};
}
function isInputFieldData(candidate) {
const inputValues = candidate;
return (inputValues.defaultValue !== undefined &&
inputValues.initialValue !== undefined &&
inputValues.currentValue !== undefined &&
inputValues.modifiers !== undefined &&
typeof inputValues.modifiers === "object" &&
Array.isArray(inputValues.modifiersOrder));
}
function constructStoreHelpers(state, fieldsCache) {
const cachedSelectField = fieldSelector => {
if (fieldSelector === FormSelector) {
return state;
}
const cachedField = fieldsCache[fieldSelector];
if (cachedField != null) {
return cachedField;
}
const selectedField = selectField(state, fieldSelector);
if (selectedField != null) {
fieldsCache[fieldSelector] = selectedField;
}
return selectedField;
};
const helpers = {
selectField: cachedSelectField,
selectFieldParent: fieldSelector => {
const parentId = getFieldParentId(fieldSelector);
if (parentId == null) {
return undefined;
}
return cachedSelectField(parentId);
},
getActiveFieldId: () => {
const formState = state;
return formState.data.activeFieldId;
},
getFieldParentId: getFieldParentId,
getFormValue: () => {
return state.getValue(state);
}
};
return helpers;
}
function constructUpdateStoreHelpers(updaters, draft, store, fieldsCache) {
const fieldStoreHelpers = constructStoreHelpers(draft, fieldsCache);
const updateStoreHelpers = Object.assign(Object.assign({}, fieldStoreHelpers), { registerField: (fieldId, initialFieldState) => {
registerField(draft, fieldId, initialFieldState);
}, unregisterField: id => {
unregisterField(draft, id);
}, setActiveFieldId: fieldId => {
const formState = draft;
formState.data.activeFieldId = fieldId;
}, updateFieldStatus: (fieldSelector, updater) => {
const statusUpdater = updateStoreHelpers.getUpdater("status");
statusUpdater.updateFieldStatus(fieldSelector, updater);
}, getUpdater: updaterId => {
return getUpdater(updateStoreHelpers, draft, store, updaters, updaterId);
}, enqueueUpdate: updater => {
setTimeout(() => store.update(updater), 0);
} });
return updateStoreHelpers;
}
function registerField(state, fieldId, initialFieldState) {
if (initialFieldState.computedValue && isInputFieldData(initialFieldState.data)) {
throw new Error(`Field ${fieldId} is marked to have computedValue, but also has data of an input field.`);
}
const fieldName = getFieldNameFromId(fieldId);
const parentField = selectRegistrationParent(state, fieldId);
if (parentField == null) {
throw new Error(`Parent for field '${fieldId}' to be registered on was not found.`);
}
const fieldState = parentField.fields[fieldName];
if (fieldState != null && fieldState.status.permanent === false) {
throw new Error(`Field '${fieldId}' has already been registered.`);
}
if (fieldState == null) {
// Make fields non-read-only
const mutableFields = parentField.fields;
// Add field into the state.
mutableFields[fieldName] = Object.assign(Object.assign(Object.assign({}, getDefaultState()), initialFieldState), { id: fieldId, name: fieldName, fields: {} });
}
}
function unregisterField(state, id) {
const parentField = selectFieldParent(state, id);
if (parentField == null) {
return;
}
const fieldName = getFieldNameFromId(id);
const field = parentField.fields[fieldName];
if (field == null) {
return;
}
if (field.status.permanent) {
return;
}
// Make fields non-read-only
const mutableFields = parentField.fields;
mutableFields[fieldName] = undefined;
}
function getUpdater(helpers, fieldState, store, updaters, updaterId) {
const factory = updaters[updaterId];
if (factory != null) {
return factory(helpers, fieldState, store);
}
return undefined;
}
function selectRegistrationParent(state, fieldId) {
const separatorIndex = fieldId.indexOf(IdSeparator);
if (separatorIndex === -1) {
return state;
}
else {
const firstChildName = fieldId.slice(0, separatorIndex);
const nextChildId = fieldId.slice(separatorIndex + IdSeparator.length);
// TODO: Review
const child = state.fields[firstChildName];
if (child == null) {
return undefined;
}
return selectRegistrationParent(child, nextChildId);
}
}
// TODO: Do we need recursion here?
function selectField(state, selector) {
if (selector === FormSelector) {
return state;
}
const fieldId = selector;
const firstSeparatorIndex = fieldId.indexOf(IdSeparator);
if (firstSeparatorIndex === -1) {
return state.fields[fieldId];
}
else {
const name = fieldId.slice(0, firstSeparatorIndex);
const nextFieldId = fieldId.slice(firstSeparatorIndex + IdSeparator.length);
const child = state.fields[name];
if (child == null) {
return undefined;
}
return selectField(child, nextFieldId);
}
}
function getFieldParentId(fieldSelector) {
if (fieldSelector === FormSelector) {
return undefined;
}
const lastSeparatorIndex = fieldSelector.lastIndexOf(IdSeparator);
if (lastSeparatorIndex === -1) {
return FormSelector;
}
return fieldSelector.slice(0, lastSeparatorIndex);
}
function selectFieldParent(state, fieldId) {
const parentId = getFieldParentId(fieldId);
if (parentId == null) {
return undefined;
}
return selectField(state, parentId);
}
class Store {
constructor(initialStateFactory, updatersFactories) {
// super();
this.emitter = new tinyEmitter.TinyEmitter();
this.handlers = {};
this.handlerIdsByFieldId = {};
this.count = 0;
this.fieldHandlersListener = (patches) => {
const fieldsKeys = Object.keys(this.handlerIdsByFieldId);
if (fieldsKeys.length === 0) {
return;
}
// Strongly type the key to catch an error if it changes.
const fieldsKey = "fields";
const paths = [];
patches.map(patch => {
const idPath = patch.path.filter(x => x !== fieldsKey).join(IdSeparator);
paths.push(idPath);
});
const fieldsToCall = [];
for (const fieldKey of fieldsKeys) {
// TODO: Is this check needed?
if (fieldsToCall.includes(fieldKey)) {
continue;
}
const fieldHandlersShouldBeCalled = paths.some(path => {
if (!path.startsWith(fieldKey)) {
return false;
}
return path.charAt(fieldKey.length) === IdSeparator;
});
if (!fieldHandlersShouldBeCalled) {
continue;
}
fieldsToCall.push(fieldKey);
}
const handlerIds = fieldsToCall
.map(fieldId => {
// We've accumulated the ids from this object, thus they exist.
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return this.handlerIdsByFieldId[fieldId];
})
.flatMap(x => x);
for (const handlerId of handlerIds) {
const handler = this.handlers[handlerId];
if (handler == null) {
continue;
}
handler(patches);
}
};
// eslint-disable-next-line @typescript-eslint/no-empty-function
this.state = produce(initialStateFactory(), () => { });
this.updaters = updatersFactories != null ? Object.assign({}, updatersFactories) : getDefaultUpdatersFactories();
this.emitter.addListener(this.fieldHandlersListener);
}
get state() {
return this._state;
}
set state(value) {
var _a;
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
// @ts-ignore
if (window.debugState === true) {
const err = new Error();
/* eslint-disable no-console */
console.groupCollapsed("State being updated:", Object.assign({}, value));
console.log((_a = err.stack) === null || _a === void 0 ? void 0 : _a.split("\n")[3]);
console.groupEnd();
/* eslint-enable no-console */
}
this._state = value;
this._helpers = constructStoreHelpers(this._state, {});
}
get helpers() {
return this._helpers;
}
getState() {
return this.state;
}
addListener(handler, dependentFields) {
const handlerId = `h${this.count++}`;
this.handlers[handlerId] = handler;
if (dependentFields == null || dependentFields.length === 0) {
return this.emitter.addListener(patches => {
handler(patches);
});
}
const unregisterCallbacks = [];
for (const fieldId of dependentFields) {
let handlerIds = this.handlerIdsByFieldId[fieldId];
if (handlerIds == null) {
handlerIds = [];
this.handlerIdsByFieldId[fieldId] = handlerIds;
}
if (handlerIds.includes(handlerId)) {
continue;
}
// Register callback
handlerIds.push(handlerId);
const unregisterCallback = () => {
if (this.handlerIdsByFieldId[fieldId] == null) {
return;
}
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion, no-shadow
const handlerIds = this.handlerIdsByFieldId[fieldId];
const index = handlerIds.indexOf(handlerId);
if (index === -1) {
return;
}
handlerIds.splice(index, 1);
};
unregisterCallbacks.push(unregisterCallback);
}
return () => {
for (const callback of unregisterCallbacks) {
callback();
}
};
}
update(updater) {
const patches = [];
const newState = produce(this.state, draft => {
updater(constructUpdateStoreHelpers(this.updaters, draft, this, {}), draft);
}, updatePatches => {
patches.push(...updatePatches);
});
if (this.state === newState) {
return;
}
this.state = newState;
this.emitter.emit(patches);
}
}
exports.CancellationTokenImpl = CancellationTokenImpl;
exports.FormSelector = FormSelector;
exports.FormsStores = FormsStores;
exports.IdSeparator = IdSeparator;
exports.StatusUpdater = StatusUpdater;
exports.StatusUpdaterFactory = StatusUpdaterFactory;
exports.Store = Store;
exports.ValidationUpdater = ValidationUpdater;
exports.ValidationUpdaterFactory = ValidationUpdaterFactory;
exports.ValueUpdater = ValueUpdater;
exports.ValueUpdaterFactory = ValueUpdaterFactory;
exports.assertFieldIsDefined = assertFieldIsDefined;
exports.assertUpdaterIsDefined = assertUpdaterIsDefined;
exports.constructFieldHelpers = constructFieldHelpers;
exports.constructInputFieldHelpers = constructInputFieldHelpers;
exports.constructStoreHelpers = constructStoreHelpers;
exports.constructUpdateStoreHelpers = constructUpdateStoreHelpers;
exports.constructValidatorHelpers = constructValidatorHelpers;
exports.formsLogger = formsLogger;
exports.generateFieldId = generateFieldId;
exports.getDefaultState = getDefaultState;
exports.getDefaultStatuses = getDefaultStatuses;
exports.getDefaultUpdatersFactories = getDefaultUpdatersFactories;
exports.getDefaultValidation = getDefaultValidation;
exports.getFieldNameFromId = getFieldNameFromId;
exports.getFieldParentId = getFieldParentId;
exports.getInitialInputData = getInitialInputData;
exports.getInputValues = getInputValues;
exports.isInputFieldData = isInputFieldData;
exports.isPromise = isPromise;
exports.selectField = selectField;
exports.selectFieldParent = selectFieldParent;

@@ -1,11 +0,2 @@

export declare const LOGGER_PREFIX = "Reactway-Forms:";
declare class Logger {
protected multiline(...args: any[]): any[];
log(...args: any[]): void;
warn(...args: any[]): void;
error(...args: any[]): void;
info(...args: any[]): void;
}
export declare const formsLogger: Logger;
export {};
//# sourceMappingURL=logger.d.ts.map
import Debug from "debug";
export declare const formsLogger: Debug.Debugger;

@@ -22,2 +22,1 @@ import { Draft, Patch } from "immer";

}
//# sourceMappingURL=store.d.ts.map
export * from "./validation-updater";
export * from "./value-updater";
export * from "./status-updater";
//# sourceMappingURL=index.d.ts.map
import { StatusUpdater, FieldState, UpdateStoreHelpers } from "../contracts";
export declare function StatusUpdaterFactory(helpers: UpdateStoreHelpers, state: FieldState<any, any>): StatusUpdater;
//# sourceMappingURL=status-updater.d.ts.map

@@ -6,2 +6,1 @@ import { FieldState, UpdateStoreHelpers, ValidationUpdater, ValidationResultOrigin, ValidatorHelpers } from "../contracts";

export declare function constructValidatorHelpers(origin: ValidationResultOrigin.Validation | ValidationResultOrigin.Unknown, validatorName: string): ValidatorHelpers;
//# sourceMappingURL=validation-updater.d.ts.map
import { ValueUpdater, UpdateStoreHelpers } from "../contracts";
export declare function ValueUpdaterFactory(helpers: UpdateStoreHelpers): ValueUpdater;
//# sourceMappingURL=value-updater.d.ts.map
{
"name": "@reactway/forms-core",
"version": "0.0.0-canary.585eb1d",
"description": "React forms.",
"main": "dist/index.js",
"module": "dist/index.es.js",
"types": "dist/index.d.ts",
"scripts": {
"build": "tsc -b .",
"test": "eslint \"src/**/*.ts*\"",
"build:prod": "rollup -c && tsc -p . --emitDeclarationOnly",
"watch": "tsc -b . -w"
},
"version": "0.0.0-canary.6baa6d9",
"author": "Reactway <dev@reactway.com> (https://github.com/reactway)",
"license": "MIT",
"files": [
"dist",
"*.js",
"*.d.ts",
"!*.config.js",
"!dist/_debug*"
],
"dependencies": {
"@reactway/tiny-emitter": "^1.0.2",
"@types/shortid": "^0.0.29",
"debug": "^4.1.1",
"immer": "^5.2.1",

@@ -30,9 +14,27 @@ "shortid": "^2.2.15"

"devDependencies": {
"@reactway-tools/rollup": "^0.0.0-canary.585eb1d",
"@reactway/eslint-config": "1.0.1",
"eslint": "6.8.0",
"prettier": "1.19.1",
"@reactway-tools/rollup": "^0.0.0-canary.6baa6d9",
"@reactway/eslint-config": "^1.0.1",
"@types/debug": "^4.1.5",
"eslint": "^6.8.0",
"prettier": "^1.19.1",
"rollup": "1.32.0",
"typescript": "3.7.5"
}
"typescript": "3.8.3"
},
"files": [
"!*.config.js",
"!dist/_debug*",
"*.d.ts",
"*.js",
"dist"
],
"license": "MIT",
"main": "dist/index.js",
"module": "dist/index.es.js",
"scripts": {
"build": "tsc -b .",
"build:prod": "rollup -c && tsc -p . --emitDeclarationOnly --declarationMap false",
"test": "eslint \"src/**/*.ts*\" --max-warnings 0",
"watch": "tsc -b . -w"
},
"types": "dist/index.d.ts"
}
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