fix: add imperativeState and replace sequenceDb global variables with it
This commit is contained in:
parent
df068dbde8
commit
b4f444869e
1
.npmrc
1
.npmrc
|
@ -1,2 +1,3 @@
|
|||
registry=https://registry.npmjs.org
|
||||
auto-install-peers=true
|
||||
strict-peer-dependencies=false
|
||||
|
|
|
@ -2,57 +2,60 @@ import * as configApi from '../../config.js';
|
|||
import { log } from '../../logger.js';
|
||||
import { sanitizeText } from '../common/common.js';
|
||||
import {
|
||||
setAccTitle,
|
||||
getAccTitle,
|
||||
setDiagramTitle,
|
||||
getDiagramTitle,
|
||||
getAccDescription,
|
||||
setAccDescription,
|
||||
clear as commonClear,
|
||||
getAccDescription,
|
||||
getAccTitle,
|
||||
getDiagramTitle,
|
||||
setAccDescription,
|
||||
setAccTitle,
|
||||
setDiagramTitle,
|
||||
} from '../common/commonDb.js';
|
||||
import { createImperativeState } from '../../utils/imperativeState.js';
|
||||
|
||||
let prevActor = undefined;
|
||||
let actors = {};
|
||||
let createdActors = {};
|
||||
let destroyedActors = {};
|
||||
let boxes = [];
|
||||
let messages = [];
|
||||
const notes = [];
|
||||
let sequenceNumbersEnabled = false;
|
||||
let wrapEnabled;
|
||||
let currentBox = undefined;
|
||||
let lastCreated = undefined;
|
||||
let lastDestroyed = undefined;
|
||||
const state = createImperativeState(() => ({
|
||||
prevActor: undefined,
|
||||
actors: {},
|
||||
createdActors: {},
|
||||
destroyedActors: {},
|
||||
boxes: [],
|
||||
messages: [],
|
||||
notes: [],
|
||||
sequenceNumbersEnabled: false,
|
||||
wrapEnabled: undefined,
|
||||
currentBox: undefined,
|
||||
lastCreated: undefined,
|
||||
lastDestroyed: undefined,
|
||||
}));
|
||||
|
||||
export const addBox = function (data) {
|
||||
boxes.push({
|
||||
state.records.boxes.push({
|
||||
name: data.text,
|
||||
wrap: (data.wrap === undefined && autoWrap()) || !!data.wrap,
|
||||
fill: data.color,
|
||||
actorKeys: [],
|
||||
});
|
||||
currentBox = boxes.slice(-1)[0];
|
||||
state.records.currentBox = state.records.boxes.slice(-1)[0];
|
||||
};
|
||||
|
||||
export const addActor = function (id, name, description, type) {
|
||||
let assignedBox = currentBox;
|
||||
const old = actors[id];
|
||||
let assignedBox = state.records.currentBox;
|
||||
const old = state.records.actors[id];
|
||||
if (old) {
|
||||
// If already set and trying to set to a new one throw error
|
||||
if (currentBox && old.box && currentBox !== old.box) {
|
||||
if (state.records.currentBox && old.box && state.records.currentBox !== old.box) {
|
||||
throw new Error(
|
||||
'A same participant should only be defined in one Box: ' +
|
||||
old.name +
|
||||
" can't be in '" +
|
||||
old.box.name +
|
||||
"' and in '" +
|
||||
currentBox.name +
|
||||
state.records.currentBox.name +
|
||||
"' at the same time."
|
||||
);
|
||||
}
|
||||
|
||||
// Don't change the box if already
|
||||
assignedBox = old.box ? old.box : currentBox;
|
||||
assignedBox = old.box ? old.box : state.records.currentBox;
|
||||
old.box = assignedBox;
|
||||
|
||||
// Don't allow description nulling
|
||||
|
@ -69,36 +72,42 @@ export const addActor = function (id, name, description, type) {
|
|||
description = { text: name, wrap: null, type };
|
||||
}
|
||||
|
||||
actors[id] = {
|
||||
state.records.actors[id] = {
|
||||
box: assignedBox,
|
||||
name: name,
|
||||
description: description.text,
|
||||
wrap: (description.wrap === undefined && autoWrap()) || !!description.wrap,
|
||||
prevActor: prevActor,
|
||||
prevActor: state.records.prevActor,
|
||||
links: {},
|
||||
properties: {},
|
||||
actorCnt: null,
|
||||
rectData: null,
|
||||
type: type || 'participant',
|
||||
};
|
||||
if (prevActor && actors[prevActor]) {
|
||||
actors[prevActor].nextActor = id;
|
||||
if (state.records.prevActor && state.records.actors[state.records.prevActor]) {
|
||||
state.records.actors[state.records.prevActor].nextActor = id;
|
||||
}
|
||||
|
||||
if (currentBox) {
|
||||
currentBox.actorKeys.push(id);
|
||||
if (state.records.currentBox) {
|
||||
state.records.currentBox.actorKeys.push(id);
|
||||
}
|
||||
prevActor = id;
|
||||
state.records.prevActor = id;
|
||||
};
|
||||
|
||||
const activationCount = (part) => {
|
||||
let i;
|
||||
let count = 0;
|
||||
for (i = 0; i < messages.length; i++) {
|
||||
if (messages[i].type === LINETYPE.ACTIVE_START && messages[i].from.actor === part) {
|
||||
for (i = 0; i < state.records.messages.length; i++) {
|
||||
if (
|
||||
state.records.messages[i].type === LINETYPE.ACTIVE_START &&
|
||||
state.records.messages[i].from.actor === part
|
||||
) {
|
||||
count++;
|
||||
}
|
||||
if (messages[i].type === LINETYPE.ACTIVE_END && messages[i].from.actor === part) {
|
||||
if (
|
||||
state.records.messages[i].type === LINETYPE.ACTIVE_END &&
|
||||
state.records.messages[i].from.actor === part
|
||||
) {
|
||||
count--;
|
||||
}
|
||||
}
|
||||
|
@ -106,7 +115,7 @@ const activationCount = (part) => {
|
|||
};
|
||||
|
||||
export const addMessage = function (idFrom, idTo, message, answer) {
|
||||
messages.push({
|
||||
state.records.messages.push({
|
||||
from: idFrom,
|
||||
to: idTo,
|
||||
message: message.text,
|
||||
|
@ -137,7 +146,7 @@ export const addSignal = function (
|
|||
throw error;
|
||||
}
|
||||
}
|
||||
messages.push({
|
||||
state.records.messages.push({
|
||||
from: idFrom,
|
||||
to: idTo,
|
||||
message: message.text,
|
||||
|
@ -149,63 +158,58 @@ export const addSignal = function (
|
|||
};
|
||||
|
||||
export const hasAtLeastOneBox = function () {
|
||||
return boxes.length > 0;
|
||||
return state.records.boxes.length > 0;
|
||||
};
|
||||
|
||||
export const hasAtLeastOneBoxWithTitle = function () {
|
||||
return boxes.some((b) => b.name);
|
||||
return state.records.boxes.some((b) => b.name);
|
||||
};
|
||||
|
||||
export const getMessages = function () {
|
||||
return messages;
|
||||
return state.records.messages;
|
||||
};
|
||||
|
||||
export const getBoxes = function () {
|
||||
return boxes;
|
||||
return state.records.boxes;
|
||||
};
|
||||
export const getActors = function () {
|
||||
return actors;
|
||||
return state.records.actors;
|
||||
};
|
||||
export const getCreatedActors = function () {
|
||||
return createdActors;
|
||||
return state.records.createdActors;
|
||||
};
|
||||
export const getDestroyedActors = function () {
|
||||
return destroyedActors;
|
||||
return state.records.destroyedActors;
|
||||
};
|
||||
export const getActor = function (id) {
|
||||
return actors[id];
|
||||
return state.records.actors[id];
|
||||
};
|
||||
export const getActorKeys = function () {
|
||||
return Object.keys(actors);
|
||||
return Object.keys(state.records.actors);
|
||||
};
|
||||
export const enableSequenceNumbers = function () {
|
||||
sequenceNumbersEnabled = true;
|
||||
state.records.sequenceNumbersEnabled = true;
|
||||
};
|
||||
export const disableSequenceNumbers = function () {
|
||||
sequenceNumbersEnabled = false;
|
||||
state.records.sequenceNumbersEnabled = false;
|
||||
};
|
||||
export const showSequenceNumbers = () => sequenceNumbersEnabled;
|
||||
export const showSequenceNumbers = () => state.records.sequenceNumbersEnabled;
|
||||
|
||||
export const setWrap = function (wrapSetting) {
|
||||
wrapEnabled = wrapSetting;
|
||||
state.records.wrapEnabled = wrapSetting;
|
||||
};
|
||||
|
||||
export const autoWrap = () => {
|
||||
// if setWrap has been called, use that value, otherwise use the value from the config
|
||||
// TODO: refactor, always use the config value let setWrap update the config value
|
||||
if (wrapEnabled !== undefined) {
|
||||
return wrapEnabled;
|
||||
if (state.records.wrapEnabled !== undefined) {
|
||||
return state.records.wrapEnabled;
|
||||
}
|
||||
return configApi.getConfig().sequence.wrap;
|
||||
};
|
||||
|
||||
export const clear = function () {
|
||||
actors = {};
|
||||
createdActors = {};
|
||||
destroyedActors = {};
|
||||
boxes = [];
|
||||
messages = [];
|
||||
sequenceNumbersEnabled = false;
|
||||
state.reset();
|
||||
commonClear();
|
||||
};
|
||||
|
||||
|
@ -247,7 +251,7 @@ export const parseBoxData = function (str) {
|
|||
}
|
||||
}
|
||||
|
||||
const boxData = {
|
||||
return {
|
||||
color: color,
|
||||
text:
|
||||
title !== undefined
|
||||
|
@ -262,7 +266,6 @@ export const parseBoxData = function (str) {
|
|||
: undefined
|
||||
: undefined,
|
||||
};
|
||||
return boxData;
|
||||
};
|
||||
|
||||
export const LINETYPE = {
|
||||
|
@ -321,8 +324,8 @@ export const addNote = function (actor, placement, message) {
|
|||
// eslint-disable-next-line unicorn/prefer-spread
|
||||
const actors = [].concat(actor, actor);
|
||||
|
||||
notes.push(note);
|
||||
messages.push({
|
||||
state.records.notes.push(note);
|
||||
state.records.messages.push({
|
||||
from: actors[0],
|
||||
to: actors[1],
|
||||
message: message.text,
|
||||
|
@ -414,7 +417,7 @@ function insertProperties(actor, properties) {
|
|||
*
|
||||
*/
|
||||
function boxEnd() {
|
||||
currentBox = undefined;
|
||||
state.records.currentBox = undefined;
|
||||
}
|
||||
|
||||
export const addDetails = function (actorId, text) {
|
||||
|
@ -468,7 +471,7 @@ export const apply = function (param) {
|
|||
} else {
|
||||
switch (param.type) {
|
||||
case 'sequenceIndex':
|
||||
messages.push({
|
||||
state.records.messages.push({
|
||||
from: undefined,
|
||||
to: undefined,
|
||||
message: {
|
||||
|
@ -484,18 +487,18 @@ export const apply = function (param) {
|
|||
addActor(param.actor, param.actor, param.description, param.draw);
|
||||
break;
|
||||
case 'createParticipant':
|
||||
if (actors[param.actor]) {
|
||||
if (state.records.actors[param.actor]) {
|
||||
throw new Error(
|
||||
"It is not possible to have actors with the same id, even if one is destroyed before the next is created. Use 'AS' aliases to simulate the behavior"
|
||||
);
|
||||
}
|
||||
lastCreated = param.actor;
|
||||
state.records.lastCreated = param.actor;
|
||||
addActor(param.actor, param.actor, param.description, param.draw);
|
||||
createdActors[param.actor] = messages.length;
|
||||
state.records.createdActors[param.actor] = state.records.messages.length;
|
||||
break;
|
||||
case 'destroyParticipant':
|
||||
lastDestroyed = param.actor;
|
||||
destroyedActors[param.actor] = messages.length;
|
||||
state.records.lastDestroyed = param.actor;
|
||||
state.records.destroyedActors[param.actor] = state.records.messages.length;
|
||||
break;
|
||||
case 'activeStart':
|
||||
addSignal(param.actor, undefined, undefined, param.signalType);
|
||||
|
@ -519,25 +522,28 @@ export const apply = function (param) {
|
|||
addDetails(param.actor, param.text);
|
||||
break;
|
||||
case 'addMessage':
|
||||
if (lastCreated) {
|
||||
if (param.to !== lastCreated) {
|
||||
if (state.records.lastCreated) {
|
||||
if (param.to !== state.records.lastCreated) {
|
||||
throw new Error(
|
||||
'The created participant ' +
|
||||
lastCreated +
|
||||
state.records.lastCreated +
|
||||
' does not have an associated creating message after its declaration. Please check the sequence diagram.'
|
||||
);
|
||||
} else {
|
||||
lastCreated = undefined;
|
||||
state.records.lastCreated = undefined;
|
||||
}
|
||||
} else if (lastDestroyed) {
|
||||
if (param.to !== lastDestroyed && param.from !== lastDestroyed) {
|
||||
} else if (state.records.lastDestroyed) {
|
||||
if (
|
||||
param.to !== state.records.lastDestroyed &&
|
||||
param.from !== state.records.lastDestroyed
|
||||
) {
|
||||
throw new Error(
|
||||
'The destroyed participant ' +
|
||||
lastDestroyed +
|
||||
state.records.lastDestroyed +
|
||||
' does not have an associated destroying message after its declaration. Please check the sequence diagram.'
|
||||
);
|
||||
} else {
|
||||
lastDestroyed = undefined;
|
||||
state.records.lastDestroyed = undefined;
|
||||
}
|
||||
}
|
||||
addSignal(param.from, param.to, param.msg, param.signalType, param.activate);
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
import { createImperativeState, domain } from './imperativeState.js';
|
||||
|
||||
describe('domain.optional', () => {
|
||||
it('should set undefined without args', () => {
|
||||
expect(domain.optional()).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should set identity with args', () => {
|
||||
const value = {};
|
||||
expect(domain.optional(value)).toEqual(value);
|
||||
});
|
||||
});
|
||||
|
||||
describe('domain.identity', () => {
|
||||
it('should set identity', () => {
|
||||
const value = {};
|
||||
expect(domain.identity(value)).toEqual(value);
|
||||
});
|
||||
});
|
||||
|
||||
describe('createImperativeState', () => {
|
||||
it('should create state with values from initializer', () => {
|
||||
const baz = {
|
||||
flag: false,
|
||||
};
|
||||
|
||||
const state = createImperativeState(() => ({
|
||||
foo: domain.optional<number>(),
|
||||
bar: domain.identity<string[]>([]),
|
||||
baz,
|
||||
}));
|
||||
|
||||
expect(state.records.foo).toBeUndefined();
|
||||
expect(state.records.bar).toEqual([]);
|
||||
expect(state.records.baz).toBe(baz);
|
||||
});
|
||||
|
||||
it('should update records', () => {
|
||||
const state = createImperativeState(() => ({
|
||||
foo: domain.optional<number>(),
|
||||
bar: domain.identity<string[]>([]),
|
||||
baz: {
|
||||
flag: false,
|
||||
},
|
||||
}));
|
||||
|
||||
state.records.foo = 5;
|
||||
state.records.bar = ['hello'];
|
||||
state.records.baz.flag = true;
|
||||
|
||||
expect(state.records.foo).toEqual(5);
|
||||
expect(state.records.bar).toEqual(['hello']);
|
||||
expect(state.records.baz).toEqual({
|
||||
flag: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('should reset records', () => {
|
||||
const state = createImperativeState(() => ({
|
||||
foo: domain.optional<number>(),
|
||||
bar: domain.identity<string[]>([]),
|
||||
baz: {
|
||||
flag: false,
|
||||
},
|
||||
}));
|
||||
|
||||
state.records.foo = 5;
|
||||
state.records.bar = ['hello'];
|
||||
state.records.baz.flag = true;
|
||||
state.reset();
|
||||
|
||||
expect(state.records.foo).toBeUndefined();
|
||||
expect(state.records.bar).toEqual([]);
|
||||
expect(state.records.baz).toEqual({
|
||||
flag: false,
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,37 @@
|
|||
export const createImperativeState = <S extends Record<string, unknown>>(init: () => S) => {
|
||||
const state = init();
|
||||
|
||||
return {
|
||||
get records() {
|
||||
return state;
|
||||
},
|
||||
reset: () => {
|
||||
Object.keys(state).forEach((key) => {
|
||||
delete state[key];
|
||||
});
|
||||
Object.entries(init()).forEach(([key, value]: [keyof S, any]) => {
|
||||
state[key] = value;
|
||||
});
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const domain = {
|
||||
optional: <V>(value?: V) => value,
|
||||
identity: <V>(value: V) => value,
|
||||
} as const;
|
||||
|
||||
/*
|
||||
const state = createImperativeState(() => ({
|
||||
foo: domain.optional<string>(),
|
||||
bar: domain.identity<number[]>([]),
|
||||
baz: domain.optional(1),
|
||||
}));
|
||||
|
||||
typeof state.records:
|
||||
{
|
||||
foo: string | undefined, // actual: undefined
|
||||
bar: number[], // actual: []
|
||||
baz: number | undefined, // actual: 1
|
||||
}
|
||||
*/
|
Loading…
Reference in New Issue