Merge pull request #130 from TomAFrench/prettier
Enforce prettier rules
This commit is contained in:
commit
db818f78fa
52 changed files with 572 additions and 452 deletions
10
.eslintrc.js
Normal file
10
.eslintrc.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
module.exports = {
|
||||
plugins: ["prettier"],
|
||||
extends: ["react-app"],
|
||||
rules: {
|
||||
"prettier/prettier": "error",
|
||||
"no-unused-vars": "off",
|
||||
"no-loop-func": "off",
|
||||
"no-restricted-globals": "off"
|
||||
}
|
||||
};
|
4
.prettierrc.json
Normal file
4
.prettierrc.json
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"singleQuote": true,
|
||||
"trailingComma": "none"
|
||||
}
|
12
package.json
12
package.json
|
@ -44,18 +44,6 @@
|
|||
"shelljs": "^0.8.2",
|
||||
"source-map-support": "^0.5.21"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "react-app",
|
||||
"rules": {
|
||||
"no-unused-vars": "off",
|
||||
"no-loop-func": "off",
|
||||
"no-restricted-globals": "off"
|
||||
}
|
||||
},
|
||||
"prettier": {
|
||||
"singleQuote": true,
|
||||
"trailingComma": "none"
|
||||
},
|
||||
"resolutions": {
|
||||
"@babel/preset-env": "^7.15.1",
|
||||
"@babel/core": "^7.15.1",
|
||||
|
|
|
@ -9,14 +9,18 @@ function getFlex(flex) {
|
|||
}
|
||||
|
||||
function Box({ flex, children, direction, style }) {
|
||||
return <div
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
...style,
|
||||
flex: getFlex(flex),
|
||||
display: 'flex',
|
||||
flexDirection: direction || 'column'
|
||||
}}
|
||||
>{children}</div>;
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Box;
|
||||
|
|
|
@ -206,7 +206,7 @@ class Debugger extends React.Component {
|
|||
height: 10,
|
||||
backgroundColor: '#303030',
|
||||
marginRight: 10,
|
||||
borderRadius: 10,
|
||||
borderRadius: 10
|
||||
}}
|
||||
/>
|
||||
<Button onClick={this.toggleRecord} style={{ marginRight: 10 }}>
|
||||
|
|
|
@ -99,7 +99,9 @@ class FatalError extends React.Component {
|
|||
.
|
||||
</P>
|
||||
<P>
|
||||
<Button onClick={() => window.Actual.relaunch()}>{buttonText}</Button>
|
||||
<Button onClick={() => window.Actual.relaunch()}>
|
||||
{buttonText}
|
||||
</Button>
|
||||
</P>
|
||||
<P isLast={true} style={{ fontSize: 11 }}>
|
||||
<Link
|
||||
|
|
|
@ -277,7 +277,4 @@ function FinancesAppWithContext(props) {
|
|||
);
|
||||
}
|
||||
|
||||
export default connect(
|
||||
null,
|
||||
actions
|
||||
)(FinancesAppWithContext);
|
||||
export default connect(null, actions)(FinancesAppWithContext);
|
||||
|
|
|
@ -642,7 +642,7 @@ const AccountHeader = React.memo(
|
|||
let searchInput = useRef(null);
|
||||
let splitsExpanded = useSplitsExpanded();
|
||||
|
||||
let canSync = syncEnabled && (account && account.account_id);
|
||||
let canSync = syncEnabled && account && account.account_id;
|
||||
if (!account) {
|
||||
// All accounts - check for any syncable account
|
||||
canSync = !!accounts.find(account => !!account.account_id);
|
||||
|
@ -1701,9 +1701,9 @@ class AccountInternal extends React.PureComponent {
|
|||
}
|
||||
showAccount={
|
||||
!accountId ||
|
||||
(accountId === 'offbudget' ||
|
||||
accountId === 'offbudget' ||
|
||||
accountId === 'budgeted' ||
|
||||
accountId === 'uncategorized')
|
||||
accountId === 'uncategorized'
|
||||
}
|
||||
isAdding={this.state.isAdding}
|
||||
isNew={this.isNew}
|
||||
|
|
|
@ -149,7 +149,11 @@ function ConfigureField({ field, op, value, dispatch, onApply }) {
|
|||
['amount-outflow', 'Amount (outflow)']
|
||||
]
|
||||
: field === 'date'
|
||||
? [['date', 'Date'], ['month', 'Month'], ['year', 'Year']]
|
||||
? [
|
||||
['date', 'Date'],
|
||||
['month', 'Month'],
|
||||
['year', 'Year']
|
||||
]
|
||||
: null
|
||||
}
|
||||
value={subfield}
|
||||
|
|
|
@ -230,8 +230,7 @@ class ManagementApp extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
export default connect(
|
||||
state => {
|
||||
export default connect(state => {
|
||||
let { modalStack } = state.modals;
|
||||
|
||||
return {
|
||||
|
@ -241,6 +240,4 @@ export default connect(
|
|||
loadingText: state.app.loadingText,
|
||||
currentModals: modalStack.map(modal => modal.name)
|
||||
};
|
||||
},
|
||||
actions
|
||||
)(ManagementApp);
|
||||
}, actions)(ManagementApp);
|
||||
|
|
|
@ -69,10 +69,7 @@ export function ConfirmPasswordForm({ buttons, onSetPassword, onError }) {
|
|||
</label>
|
||||
<View style={{ flex: 1 }} />
|
||||
{buttons}
|
||||
<ButtonWithLoading
|
||||
primary
|
||||
loading={loading}
|
||||
>
|
||||
<ButtonWithLoading primary loading={loading}>
|
||||
OK
|
||||
</ButtonWithLoading>
|
||||
</View>
|
||||
|
|
|
@ -70,7 +70,4 @@ function WelcomeScreen({ modalProps, actions }) {
|
|||
);
|
||||
}
|
||||
|
||||
export default connect(
|
||||
null,
|
||||
actions
|
||||
)(WelcomeScreen);
|
||||
export default connect(null, actions)(WelcomeScreen);
|
||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
|||
import { styles } from 'loot-design/src/style';
|
||||
import { integerToCurrency } from 'loot-core/src/shared/util';
|
||||
import { Block } from 'loot-design/src/components/common';
|
||||
import { colors } from 'loot-design/src/style'
|
||||
import { colors } from 'loot-design/src/style';
|
||||
|
||||
function Change({ amount, style }) {
|
||||
return (
|
||||
|
|
|
@ -110,7 +110,10 @@ function CashFlowCard() {
|
|||
const end = monthUtils.currentDay();
|
||||
const start = monthUtils.currentMonth() + '-01';
|
||||
|
||||
const data = useReport('cash_flow_simple', useArgsMemo(simpleCashFlow)(start, end));
|
||||
const data = useReport(
|
||||
'cash_flow_simple',
|
||||
useArgsMemo(simpleCashFlow)(start, end)
|
||||
);
|
||||
if (!data) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -15,9 +15,9 @@ function BudgetInitial({ accounts, navigationProps }) {
|
|||
{!minimized && (
|
||||
<React.Fragment>
|
||||
<P>
|
||||
You should see all of your current accounts' balance available
|
||||
to budget. Click on the budgeted column for a category create a
|
||||
budget. Keep doing this until your "To Budget" amount is zero.
|
||||
You should see all of your current accounts' balance available to
|
||||
budget. Click on the budgeted column for a category create a budget.
|
||||
Keep doing this until your "To Budget" amount is zero.
|
||||
</P>
|
||||
<P>
|
||||
Don't worry too much about your initial budget. Just guess. You'll
|
||||
|
|
|
@ -61,7 +61,6 @@ function BudgetNextMonth({ stepTwo, navigationProps }) {
|
|||
);
|
||||
}
|
||||
|
||||
export default connect(
|
||||
null,
|
||||
dispatch => bindActionCreators(actions, dispatch)
|
||||
)(BudgetNextMonth);
|
||||
export default connect(null, dispatch => bindActionCreators(actions, dispatch))(
|
||||
BudgetNextMonth
|
||||
);
|
||||
|
|
|
@ -5,7 +5,7 @@ import { styles, colors } from 'loot-design/src/style';
|
|||
import { Standalone, Title, useMinimized } from './common';
|
||||
|
||||
function CategoryBalance({ targetRect, navigationProps }) {
|
||||
let [minimized, toggle] = useMinimized()
|
||||
let [minimized, toggle] = useMinimized();
|
||||
|
||||
return (
|
||||
<Standalone>
|
||||
|
|
|
@ -96,7 +96,6 @@ function Overspending({ navigationProps, stepTwo }) {
|
|||
);
|
||||
}
|
||||
|
||||
export default connect(
|
||||
null,
|
||||
dispatch => bindActionCreators(actions, dispatch)
|
||||
)(Overspending);
|
||||
export default connect(null, dispatch => bindActionCreators(actions, dispatch))(
|
||||
Overspending
|
||||
);
|
||||
|
|
|
@ -33,8 +33,8 @@ function TransactionAdd({ targetRect, navigationProps }) {
|
|||
</P>
|
||||
|
||||
<P isLast={true}>
|
||||
Try <strong>clicking "Add New"</strong> to see how adding
|
||||
transactions affects your budget.
|
||||
Try <strong>clicking "Add New"</strong> to see how adding transactions
|
||||
affects your budget.
|
||||
</P>
|
||||
|
||||
<Navigation {...navigationProps} showBack={false} />
|
||||
|
|
|
@ -3,7 +3,7 @@ import { P } from 'loot-design/src/components/common';
|
|||
import { css } from 'glamor';
|
||||
import Navigation from './Navigation';
|
||||
import * as monthUtils from 'loot-core/src/shared/months';
|
||||
import {Standalone} from './common';
|
||||
import { Standalone } from './common';
|
||||
|
||||
function TransactionFinalize({ navigationProps }) {
|
||||
return (
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
"damerau-levenshtein": "^1.0.4",
|
||||
"date-fns": "2.0.0-alpha.27",
|
||||
"eslint": "5.6.0",
|
||||
"eslint-plugin-prettier": "^3.1.4",
|
||||
"esm": "^3.0.82",
|
||||
"fake-indexeddb": "^3.1.3",
|
||||
"fast-check": "^2.11.0",
|
||||
|
@ -60,6 +61,7 @@
|
|||
"mockdate": "^3.0.5",
|
||||
"murmurhash": "^0.0.2",
|
||||
"perf-deets": "^1.0.15",
|
||||
"prettier": "^1.19.1",
|
||||
"sanitize-filename": "^1.6.1",
|
||||
"search-query-parser": "^1.3.0",
|
||||
"snapshot-diff": "^0.2.2",
|
||||
|
|
|
@ -5,5 +5,5 @@ export function debugCell(sheet, name) {
|
|||
type: constants.DEBUG_CELL,
|
||||
sheet,
|
||||
name
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -439,15 +439,19 @@ async function fillOther(handlers, account, payees, groups) {
|
|||
|
||||
async function createBudget(accounts, payees, groups) {
|
||||
let primaryAccount = accounts.find(a => (a.name = 'Bank of America'));
|
||||
let earliestDate = (await db.first(
|
||||
let earliestDate = (
|
||||
await db.first(
|
||||
`SELECT * FROM v_transactions t LEFT JOIN accounts a ON t.account = a.id
|
||||
WHERE a.offbudget = 0 AND t.is_child = 0 ORDER BY date ASC LIMIT 1`
|
||||
)).date;
|
||||
let earliestPrimaryDate = (await db.first(
|
||||
)
|
||||
).date;
|
||||
let earliestPrimaryDate = (
|
||||
await db.first(
|
||||
`SELECT * FROM v_transactions t LEFT JOIN accounts a ON t.account = a.id
|
||||
WHERE a.id = ? AND a.offbudget = 0 AND t.is_child = 0 ORDER BY date ASC LIMIT 1`,
|
||||
[primaryAccount.id]
|
||||
)).date;
|
||||
)
|
||||
).date;
|
||||
|
||||
let start = monthUtils.monthFromDate(db.fromDateRepr(earliestDate));
|
||||
let end = monthUtils.currentMonth();
|
||||
|
|
|
@ -172,7 +172,10 @@ module.exports.listen = function listen(name, cb) {
|
|||
|
||||
return () => {
|
||||
let arr = listeners.get(name);
|
||||
listeners.set(name, arr.filter(cb_ => cb_ !== cb));
|
||||
listeners.set(
|
||||
name,
|
||||
arr.filter(cb_ => cb_ !== cb)
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -82,7 +82,10 @@ function listen(name, cb) {
|
|||
|
||||
return () => {
|
||||
let arr = listeners.get(name);
|
||||
listeners.set(name, arr.filter(cb_ => cb_ !== cb));
|
||||
listeners.set(
|
||||
name,
|
||||
arr.filter(cb_ => cb_ !== cb)
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -56,6 +56,9 @@ module.exports.listen = function listen(name, cb) {
|
|||
|
||||
return () => {
|
||||
let arr = listeners.get(name);
|
||||
listeners.set(name, arr.filter(cb_ => cb_ !== cb));
|
||||
listeners.set(
|
||||
name,
|
||||
arr.filter(cb_ => cb_ !== cb)
|
||||
);
|
||||
};
|
||||
};
|
||||
|
|
|
@ -117,7 +117,10 @@ module.exports.listen = function listen(name, cb) {
|
|||
return () => {
|
||||
let arr = listeners.get(name);
|
||||
if (arr) {
|
||||
listeners.set(name, arr.filter(cb_ => cb_ !== cb));
|
||||
listeners.set(
|
||||
name,
|
||||
arr.filter(cb_ => cb_ !== cb)
|
||||
);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
export function captureException(exc) {
|
||||
}
|
||||
export function captureException(exc) {}
|
||||
|
||||
export function captureBreadcrumb(info) {
|
||||
}
|
||||
export function captureBreadcrumb(info) {}
|
||||
|
|
|
@ -167,9 +167,9 @@ async function _removeFile(filepath) {
|
|||
|
||||
// Load files from the server that should exist by default
|
||||
async function populateDefaultFilesystem() {
|
||||
let index = await (await fetch(
|
||||
process.env.PUBLIC_URL + 'data-file-index.txt'
|
||||
)).text();
|
||||
let index = await (
|
||||
await fetch(process.env.PUBLIC_URL + 'data-file-index.txt')
|
||||
).text();
|
||||
let files = index
|
||||
.split('\n')
|
||||
.map(name => name.trim())
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
const conjunctions = [
|
||||
'for',
|
||||
'for', //
|
||||
'and',
|
||||
'nor',
|
||||
'but',
|
||||
'or',
|
||||
'yet',
|
||||
'so'
|
||||
]
|
||||
];
|
||||
|
||||
const articles = [
|
||||
'a',
|
||||
'a', //
|
||||
'an',
|
||||
'the'
|
||||
]
|
||||
];
|
||||
|
||||
const prepositions = [
|
||||
'aboard',
|
||||
|
@ -84,10 +84,6 @@ const prepositions = [
|
|||
'with',
|
||||
'within',
|
||||
'without'
|
||||
]
|
||||
];
|
||||
|
||||
module.exports = new Set([
|
||||
...conjunctions,
|
||||
...articles,
|
||||
...prepositions
|
||||
])
|
||||
module.exports = new Set([...conjunctions, ...articles, ...prepositions]);
|
||||
|
|
|
@ -116,9 +116,9 @@ export async function batchUpdateTransactions({
|
|||
await Promise.all(allAdded.map(t => transfer.onInsert(t)));
|
||||
|
||||
// Return any updates from here
|
||||
resultUpdated = (await Promise.all(
|
||||
allUpdated.map(t => transfer.onUpdate(t))
|
||||
)).filter(Boolean);
|
||||
resultUpdated = (
|
||||
await Promise.all(allUpdated.map(t => transfer.onUpdate(t)))
|
||||
).filter(Boolean);
|
||||
|
||||
await Promise.all(allDeleted.map(t => transfer.onDelete(t)));
|
||||
});
|
||||
|
|
|
@ -6,24 +6,28 @@ async function getPayee(acct) {
|
|||
|
||||
async function getTransferredAccount(transaction) {
|
||||
if (transaction.payee) {
|
||||
let { transfer_acct, id } = await db.first(
|
||||
'SELECT id, transfer_acct FROM v_payees WHERE id = ?',
|
||||
[transaction.payee]
|
||||
);
|
||||
let {
|
||||
transfer_acct,
|
||||
id
|
||||
} = await db.first('SELECT id, transfer_acct FROM v_payees WHERE id = ?', [
|
||||
transaction.payee
|
||||
]);
|
||||
return transfer_acct;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async function clearCategory(transaction, transferAcct) {
|
||||
const { offbudget: fromOffBudget } = await db.first(
|
||||
'SELECT offbudget FROM accounts WHERE id = ?',
|
||||
[transaction.account]
|
||||
);
|
||||
const { offbudget: toOffBudget } = await db.first(
|
||||
'SELECT offbudget FROM accounts WHERE id = ?',
|
||||
[transferAcct]
|
||||
);
|
||||
const {
|
||||
offbudget: fromOffBudget
|
||||
} = await db.first('SELECT offbudget FROM accounts WHERE id = ?', [
|
||||
transaction.account
|
||||
]);
|
||||
const {
|
||||
offbudget: toOffBudget
|
||||
} = await db.first('SELECT offbudget FROM accounts WHERE id = ?', [
|
||||
transferAcct
|
||||
]);
|
||||
|
||||
// We should clear the category to make sure it's not being
|
||||
// accounted for in the budget, unless it should be in the case of
|
||||
|
@ -36,10 +40,11 @@ async function clearCategory(transaction, transferAcct) {
|
|||
}
|
||||
|
||||
export async function addTransfer(transaction, transferredAccount) {
|
||||
let { id: fromPayee } = await db.first(
|
||||
'SELECT id FROM payees WHERE transfer_acct = ?',
|
||||
[transaction.account]
|
||||
);
|
||||
let {
|
||||
id: fromPayee
|
||||
} = await db.first('SELECT id FROM payees WHERE transfer_acct = ?', [
|
||||
transaction.account
|
||||
]);
|
||||
|
||||
// We need to enforce certain constraints with child transaction transfers
|
||||
if (transaction.parent_id) {
|
||||
|
|
|
@ -81,28 +81,34 @@ describe('runQuery', () => {
|
|||
expect(data[0].date).toBe('2020-01-04');
|
||||
|
||||
// date-month
|
||||
data = (await runQuery(
|
||||
data = (
|
||||
await runQuery(
|
||||
query('transactions')
|
||||
.select({ month: { $month: '$date' } })
|
||||
.serialize()
|
||||
)).data;
|
||||
)
|
||||
).data;
|
||||
expect(data[0].month).toBe('2020-01');
|
||||
|
||||
// date-year
|
||||
data = (await runQuery(
|
||||
data = (
|
||||
await runQuery(
|
||||
query('transactions')
|
||||
.select({ year: { $year: '$date' } })
|
||||
.serialize()
|
||||
)).data;
|
||||
)
|
||||
).data;
|
||||
expect(data[0].year).toBe('2020');
|
||||
|
||||
// boolean
|
||||
data = (await runQuery(
|
||||
data = (
|
||||
await runQuery(
|
||||
query('transactions')
|
||||
.select(['is_child', 'is_parent'])
|
||||
.raw()
|
||||
.serialize()
|
||||
)).data;
|
||||
)
|
||||
).data;
|
||||
expect(data[0].is_child).toBe(false);
|
||||
expect(data[0].is_parent).toBe(true);
|
||||
expect(data[1].is_child).toBe(true);
|
||||
|
@ -128,31 +134,37 @@ describe('runQuery', () => {
|
|||
);
|
||||
expect(data[0].id).toBe(transId);
|
||||
|
||||
data = (await runQuery(
|
||||
data = (
|
||||
await runQuery(
|
||||
query('transactions')
|
||||
.filter({ date: { $transform: '$month', $eq: { $month: ':month' } } })
|
||||
.select('date')
|
||||
.serialize(),
|
||||
{ params: { month: '2020-01-02' } }
|
||||
)).data;
|
||||
)
|
||||
).data;
|
||||
expect(data[0].id).toBe(transId);
|
||||
|
||||
data = (await runQuery(
|
||||
data = (
|
||||
await runQuery(
|
||||
query('transactions')
|
||||
.filter({ date: { $transform: '$year', $eq: { $year: ':month' } } })
|
||||
.select('date')
|
||||
.serialize(),
|
||||
{ params: { month: '2020-01-02' } }
|
||||
)).data;
|
||||
)
|
||||
).data;
|
||||
expect(data[0].id).toBe(transId);
|
||||
|
||||
data = (await runQuery(
|
||||
data = (
|
||||
await runQuery(
|
||||
query('transactions')
|
||||
.filter({ cleared: ':cleared' })
|
||||
.select('date')
|
||||
.serialize(),
|
||||
{ params: { cleared: true } }
|
||||
)).data;
|
||||
)
|
||||
).data;
|
||||
expect(data[0].id).toBe(transId);
|
||||
});
|
||||
|
||||
|
|
|
@ -142,7 +142,10 @@ async function execTransactionsGrouped(
|
|||
|
||||
rows = await db.all(rowSql, params);
|
||||
matched = new Set(
|
||||
[].concat.apply([], rows.map(row => row.matched.split(',')))
|
||||
[].concat.apply(
|
||||
[],
|
||||
rows.map(row => row.matched.split(','))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ describe('schema', () => {
|
|||
test('never returns transactions without a date', async () => {
|
||||
expect((await db.all('SELECT * FROM transactions')).length).toBe(0);
|
||||
expect((await db.all('SELECT * FROM v_transactions')).length).toBe(0);
|
||||
await db.runQuery('INSERT INTO transactions (acct) VALUES (?)', ["foo"]);
|
||||
await db.runQuery('INSERT INTO transactions (acct) VALUES (?)', ['foo']);
|
||||
expect((await db.all('SELECT * FROM transactions')).length).toBe(1);
|
||||
expect((await db.all('SELECT * FROM v_transactions')).length).toBe(0);
|
||||
});
|
||||
|
@ -28,7 +28,7 @@ describe('schema', () => {
|
|||
expect((await db.all('SELECT * FROM v_transactions')).length).toBe(0);
|
||||
await db.runQuery(
|
||||
'INSERT INTO transactions (date, acct, isChild) VALUES (?, ?, ?)',
|
||||
[20200101, "foo", 1]
|
||||
[20200101, 'foo', 1]
|
||||
);
|
||||
expect((await db.all('SELECT * FROM transactions')).length).toBe(1);
|
||||
expect((await db.all('SELECT * FROM v_transactions')).length).toBe(0);
|
||||
|
|
|
@ -3,7 +3,6 @@ import { mutator } from '../mutators';
|
|||
import { undoable } from '../undo';
|
||||
import * as actions from './actions';
|
||||
|
||||
|
||||
let app = createApp();
|
||||
|
||||
app.method('budget/budget-amount', mutator(undoable(actions.setBudget)));
|
||||
|
|
|
@ -19,4 +19,3 @@ export function unflatten2(arr) {
|
|||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,12 @@
|
|||
import * as merkle from "./merkle";
|
||||
import * as merkle from './merkle';
|
||||
|
||||
export { merkle };
|
||||
export { getClock, setClock, makeClock, makeClientId, serializeClock, deserializeClock, Timestamp } from "./timestamp"
|
||||
export {
|
||||
getClock,
|
||||
setClock,
|
||||
makeClock,
|
||||
makeClientId,
|
||||
serializeClock,
|
||||
deserializeClock,
|
||||
Timestamp
|
||||
} from './timestamp';
|
||||
|
|
|
@ -30,7 +30,16 @@ import * as bankSync from './accounts/sync';
|
|||
import * as link from './accounts/link';
|
||||
import { uniqueFileName, idFromFileName } from './util/budget-name';
|
||||
import { mutator, runHandler } from './mutators';
|
||||
import { getClock, setClock, makeClock, makeClientId, serializeClock, deserializeClock, Timestamp, merkle } from './crdt';
|
||||
import {
|
||||
getClock,
|
||||
setClock,
|
||||
makeClock,
|
||||
makeClientId,
|
||||
serializeClock,
|
||||
deserializeClock,
|
||||
Timestamp,
|
||||
merkle
|
||||
} from './crdt';
|
||||
import {
|
||||
initialFullSync,
|
||||
fullSync,
|
||||
|
@ -414,7 +423,10 @@ handlers['category-group-delete'] = mutator(async function({ id, transferId }) {
|
|||
|
||||
return batchMessages(async () => {
|
||||
if (transferId) {
|
||||
await budget.doTransfer(groupCategories.map(c => c.id), transferId);
|
||||
await budget.doTransfer(
|
||||
groupCategories.map(c => c.id),
|
||||
transferId
|
||||
);
|
||||
}
|
||||
await db.deleteCategoryGroup({ id }, transferId);
|
||||
});
|
||||
|
@ -759,11 +771,15 @@ handlers['accounts-get'] = async function() {
|
|||
};
|
||||
|
||||
handlers['account-properties'] = async function({ id }) {
|
||||
const { balance } = await db.first(
|
||||
const {
|
||||
balance
|
||||
} = await db.first(
|
||||
'SELECT sum(amount) as balance FROM transactions WHERE acct = ? AND isParent = 0 AND tombstone = 0',
|
||||
[id]
|
||||
);
|
||||
const { count } = await db.first(
|
||||
const {
|
||||
count
|
||||
} = await db.first(
|
||||
'SELECT count(id) as count FROM transactions WHERE acct = ? AND tombstone = 0',
|
||||
[id]
|
||||
);
|
||||
|
@ -902,10 +918,9 @@ handlers['account-close'] = mutator(async function({
|
|||
true
|
||||
);
|
||||
|
||||
let { id: payeeId } = await db.first(
|
||||
'SELECT id FROM payees WHERE transfer_acct = ?',
|
||||
[id]
|
||||
);
|
||||
let {
|
||||
id: payeeId
|
||||
} = await db.first('SELECT id FROM payees WHERE transfer_acct = ?', [id]);
|
||||
|
||||
await batchMessages(() => {
|
||||
// TODO: what this should really do is send a special message that
|
||||
|
@ -939,10 +954,11 @@ handlers['account-close'] = mutator(async function({
|
|||
// If there is a balance we need to transfer it to the specified
|
||||
// account (and possibly categorize it)
|
||||
if (balance !== 0) {
|
||||
let { id: payeeId } = await db.first(
|
||||
'SELECT id FROM payees WHERE transfer_acct = ?',
|
||||
[transferAccountId]
|
||||
);
|
||||
let {
|
||||
id: payeeId
|
||||
} = await db.first('SELECT id FROM payees WHERE transfer_acct = ?', [
|
||||
transferAccountId
|
||||
]);
|
||||
|
||||
await handlers['transaction-add']({
|
||||
id: uuid.v4Sync(),
|
||||
|
@ -1082,9 +1098,7 @@ handlers['accounts-sync'] = async function({ id }) {
|
|||
} else if (err instanceof PostError && err.reason !== 'internal') {
|
||||
errors.push({
|
||||
accountId: acct.id,
|
||||
message: `Account "${
|
||||
acct.name
|
||||
}" is not linked properly. Please link it again`
|
||||
message: `Account "${acct.name}" is not linked properly. Please link it again`
|
||||
});
|
||||
} else {
|
||||
errors.push({
|
||||
|
@ -1134,10 +1148,9 @@ handlers['transactions-import'] = mutator(function({
|
|||
});
|
||||
|
||||
handlers['account-unlink'] = mutator(async function({ id }) {
|
||||
let { bank: bankId } = await db.first(
|
||||
'SELECT bank FROM accounts WHERE id = ?',
|
||||
[id]
|
||||
);
|
||||
let {
|
||||
bank: bankId
|
||||
} = await db.first('SELECT bank FROM accounts WHERE id = ?', [id]);
|
||||
|
||||
if (!bankId) {
|
||||
return 'ok';
|
||||
|
@ -1152,10 +1165,11 @@ handlers['account-unlink'] = mutator(async function({ id }) {
|
|||
balance_limit: null
|
||||
});
|
||||
|
||||
let { count } = await db.first(
|
||||
'SELECT COUNT(*) as count FROM accounts WHERE bank = ?',
|
||||
[bankId]
|
||||
);
|
||||
let {
|
||||
count
|
||||
} = await db.first('SELECT COUNT(*) as count FROM accounts WHERE bank = ?', [
|
||||
bankId
|
||||
]);
|
||||
|
||||
if (count === 0) {
|
||||
// No more accounts are associated with this bank. We can remove
|
||||
|
@ -1545,7 +1559,8 @@ handlers['get-version'] = async function() {
|
|||
|
||||
handlers['get-budgets'] = async function() {
|
||||
const paths = await fs.listDir(fs.getDocumentDir());
|
||||
const budgets = (await Promise.all(
|
||||
const budgets = (
|
||||
await Promise.all(
|
||||
paths.map(async name => {
|
||||
const prefsPath = fs.join(fs.getDocumentDir(), name, 'metadata.json');
|
||||
if (await fs.exists(prefsPath)) {
|
||||
|
@ -1573,7 +1588,8 @@ handlers['get-budgets'] = async function() {
|
|||
|
||||
return null;
|
||||
})
|
||||
)).filter(x => x);
|
||||
)
|
||||
).filter(x => x);
|
||||
|
||||
return budgets;
|
||||
};
|
||||
|
@ -2253,7 +2269,13 @@ export const lib = {
|
|||
// Expose CRDT mechanisms so server can use them
|
||||
merkle,
|
||||
timestamp: {
|
||||
getClock, setClock, makeClock, makeClientId, serializeClock, deserializeClock, Timestamp
|
||||
getClock,
|
||||
setClock,
|
||||
makeClock,
|
||||
makeClientId,
|
||||
serializeClock,
|
||||
deserializeClock,
|
||||
Timestamp
|
||||
},
|
||||
SyncProtoBuf: SyncPb
|
||||
};
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
// Mobile needs this
|
||||
import 'core-js/modules/es.object.from-entries'
|
||||
import 'core-js/modules/es.object.from-entries';
|
||||
|
|
|
@ -71,7 +71,10 @@ describe('schedule app', () => {
|
|||
value: {
|
||||
start: '2020-12-20',
|
||||
frequency: 'monthly',
|
||||
patterns: [{ type: 'day', value: 15 }, { type: 'day', value: 30 }]
|
||||
patterns: [
|
||||
{ type: 'day', value: 15 },
|
||||
{ type: 'day', value: 30 }
|
||||
]
|
||||
}
|
||||
})
|
||||
).toBe('2021-05-30');
|
||||
|
@ -88,7 +91,10 @@ describe('schedule app', () => {
|
|||
value: {
|
||||
start: '2020-12-20',
|
||||
frequency: 'monthly',
|
||||
patterns: [{ type: 'day', value: 15 }, { type: 'day', value: 30 }]
|
||||
patterns: [
|
||||
{ type: 'day', value: 15 },
|
||||
{ type: 'day', value: 30 }
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
|
@ -123,7 +129,10 @@ describe('schedule app', () => {
|
|||
value: {
|
||||
start: '2020-12-20',
|
||||
frequency: 'monthly',
|
||||
patterns: [{ type: 'day', value: 15 }, { type: 'day', value: 30 }]
|
||||
patterns: [
|
||||
{ type: 'day', value: 15 },
|
||||
{ type: 'day', value: 30 }
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
|
@ -150,7 +159,10 @@ describe('schedule app', () => {
|
|||
value: {
|
||||
start: '2020-12-20',
|
||||
frequency: 'monthly',
|
||||
patterns: [{ type: 'day', value: 18 }, { type: 'day', value: 29 }]
|
||||
patterns: [
|
||||
{ type: 'day', value: 18 },
|
||||
{ type: 'day', value: 29 }
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
|
@ -177,7 +189,10 @@ describe('schedule app', () => {
|
|||
value: {
|
||||
start: '2020-12-20',
|
||||
frequency: 'monthly',
|
||||
patterns: [{ type: 'day', value: 15 }, { type: 'day', value: 30 }]
|
||||
patterns: [
|
||||
{ type: 'day', value: 15 },
|
||||
{ type: 'day', value: 30 }
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
|
@ -200,7 +215,10 @@ describe('schedule app', () => {
|
|||
value: {
|
||||
start: '2020-12-20',
|
||||
frequency: 'monthly',
|
||||
patterns: [{ type: 'day', value: 15 }, { type: 'day', value: 30 }]
|
||||
patterns: [
|
||||
{ type: 'day', value: 15 },
|
||||
{ type: 'day', value: 30 }
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
|
@ -222,7 +240,10 @@ describe('schedule app', () => {
|
|||
value: {
|
||||
start: '2020-12-20',
|
||||
frequency: 'monthly',
|
||||
patterns: [{ type: 'day', value: 18 }, { type: 'day', value: 28 }]
|
||||
patterns: [
|
||||
{ type: 'day', value: 18 },
|
||||
{ type: 'day', value: 28 }
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
@ -219,7 +219,10 @@ async function monthly1stor3rd(startDate, accountId) {
|
|||
return {
|
||||
start,
|
||||
frequency: 'monthly',
|
||||
patterns: [{ type: dayValue, value: 1 }, { type: dayValue, value: 3 }]
|
||||
patterns: [
|
||||
{ type: dayValue, value: 1 },
|
||||
{ type: dayValue, value: 3 }
|
||||
]
|
||||
};
|
||||
},
|
||||
accountId
|
||||
|
@ -237,7 +240,10 @@ async function monthly2ndor4th(startDate, accountId) {
|
|||
return {
|
||||
start,
|
||||
frequency: 'monthly',
|
||||
patterns: [{ type: dayValue, value: 2 }, { type: dayValue, value: 4 }]
|
||||
patterns: [
|
||||
{ type: dayValue, value: 2 },
|
||||
{ type: dayValue, value: 4 }
|
||||
]
|
||||
};
|
||||
},
|
||||
accountId
|
||||
|
|
|
@ -363,7 +363,7 @@ function parsePostfix(state, node) {
|
|||
while ((tok = nextToken(state))) {
|
||||
if (tok.type === types.TOKEN_LEFT_PAREN) {
|
||||
pushToken(state, tok);
|
||||
let args = parseArgs(state)
|
||||
let args = parseArgs(state);
|
||||
node = new nodes.FunCall(tok.lineno, tok.colno, node, args);
|
||||
} else if (tok.type === types.TOKEN_DOT) {
|
||||
const val = nextToken(state);
|
||||
|
|
|
@ -238,9 +238,7 @@ export default function generate(table, where, groupby, select, deps) {
|
|||
joins.push(meta.sql(lookup.tableId));
|
||||
} else {
|
||||
joins.push(
|
||||
`LEFT JOIN ${meta.table} ${lookup.tableId} ON ${
|
||||
lookup.tableId
|
||||
}.id = ${currentTable.id}.${lookup.field}`
|
||||
`LEFT JOIN ${meta.table} ${lookup.tableId} ON ${lookup.tableId}.id = ${currentTable.id}.${lookup.field}`
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -74,7 +74,10 @@ export default class VM {
|
|||
|
||||
call(callee, args) {
|
||||
const func = this.get(callee);
|
||||
this.reg1 = func.apply(null, args.map(arg => this.get(arg)));
|
||||
this.reg1 = func.apply(
|
||||
null,
|
||||
args.map(arg => this.get(arg))
|
||||
);
|
||||
}
|
||||
|
||||
query(sql, calculated) {
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
const expect = require('expect');
|
||||
const propagate = require('../data-compute/propagate.js');
|
||||
|
||||
|
||||
const expect = require("expect");
|
||||
const propagate = require("../data-compute/propagate.js");
|
||||
|
||||
describe("data propagation", () => {
|
||||
it("should work", () => {
|
||||
describe('data propagation', () => {
|
||||
it('should work', () => {
|
||||
expect(true).toExist();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,308 +1,304 @@
|
|||
|
||||
// Unit tests for reactive-property.
|
||||
var assert = require("assert");
|
||||
var assert = require('assert');
|
||||
|
||||
// If using from the NPM package, this line would be
|
||||
// var Graph = require("graph-data-structure");
|
||||
var Graph = require("../data-compute/graph-data-structure");
|
||||
var Graph = require('../data-compute/graph-data-structure');
|
||||
|
||||
describe("Graph", function() {
|
||||
describe("Data structure", function() {
|
||||
it("Should add nodes and list them.", function (){
|
||||
describe('Graph', function() {
|
||||
describe('Data structure', function() {
|
||||
it('Should add nodes and list them.', function() {
|
||||
var graph = Graph();
|
||||
graph.addNode("a");
|
||||
graph.addNode("b");
|
||||
graph.addNode('a');
|
||||
graph.addNode('b');
|
||||
assert.equal(graph.nodes().length, 2);
|
||||
assert(contains(graph.nodes(), "a"));
|
||||
assert(contains(graph.nodes(), "b"));
|
||||
assert(contains(graph.nodes(), 'a'));
|
||||
assert(contains(graph.nodes(), 'b'));
|
||||
});
|
||||
|
||||
it("Should chain addNode.", function (){
|
||||
var graph = Graph().addNode("a").addNode("b");
|
||||
it('Should chain addNode.', function() {
|
||||
var graph = Graph()
|
||||
.addNode('a')
|
||||
.addNode('b');
|
||||
assert.equal(graph.nodes().length, 2);
|
||||
assert(contains(graph.nodes(), "a"));
|
||||
assert(contains(graph.nodes(), "b"));
|
||||
assert(contains(graph.nodes(), 'a'));
|
||||
assert(contains(graph.nodes(), 'b'));
|
||||
});
|
||||
|
||||
it("Should remove nodes.", function (){
|
||||
it('Should remove nodes.', function() {
|
||||
var graph = Graph();
|
||||
graph.addNode("a");
|
||||
graph.addNode("b");
|
||||
graph.removeNode("a");
|
||||
graph.removeNode("b");
|
||||
graph.addNode('a');
|
||||
graph.addNode('b');
|
||||
graph.removeNode('a');
|
||||
graph.removeNode('b');
|
||||
assert.equal(graph.nodes().length, 0);
|
||||
});
|
||||
|
||||
it("Should chain removeNode.", function (){
|
||||
it('Should chain removeNode.', function() {
|
||||
var graph = Graph()
|
||||
.addNode("a")
|
||||
.addNode("b")
|
||||
.removeNode("a")
|
||||
.removeNode("b");
|
||||
.addNode('a')
|
||||
.addNode('b')
|
||||
.removeNode('a')
|
||||
.removeNode('b');
|
||||
assert.equal(graph.nodes().length, 0);
|
||||
});
|
||||
|
||||
it("Should add edges and query for adjacent nodes.", function (){
|
||||
it('Should add edges and query for adjacent nodes.', function() {
|
||||
var graph = Graph();
|
||||
graph.addNode("a");
|
||||
graph.addNode("b");
|
||||
graph.addEdge("a", "b");
|
||||
assert.equal(graph.adjacent("a").length, 1);
|
||||
assert.equal(graph.adjacent("a")[0], "b");
|
||||
graph.addNode('a');
|
||||
graph.addNode('b');
|
||||
graph.addEdge('a', 'b');
|
||||
assert.equal(graph.adjacent('a').length, 1);
|
||||
assert.equal(graph.adjacent('a')[0], 'b');
|
||||
});
|
||||
|
||||
it("Should implicitly add nodes when edges are added.", function (){
|
||||
it('Should implicitly add nodes when edges are added.', function() {
|
||||
var graph = Graph();
|
||||
graph.addEdge("a", "b");
|
||||
assert.equal(graph.adjacent("a").length, 1);
|
||||
assert.equal(graph.adjacent("a")[0], "b");
|
||||
graph.addEdge('a', 'b');
|
||||
assert.equal(graph.adjacent('a').length, 1);
|
||||
assert.equal(graph.adjacent('a')[0], 'b');
|
||||
assert.equal(graph.nodes().length, 2);
|
||||
assert(contains(graph.nodes(), "a"));
|
||||
assert(contains(graph.nodes(), "b"));
|
||||
assert(contains(graph.nodes(), 'a'));
|
||||
assert(contains(graph.nodes(), 'b'));
|
||||
});
|
||||
|
||||
it("Should chain addEdge.", function (){
|
||||
var graph = Graph().addEdge("a", "b");
|
||||
assert.equal(graph.adjacent("a").length, 1);
|
||||
assert.equal(graph.adjacent("a")[0], "b");
|
||||
it('Should chain addEdge.', function() {
|
||||
var graph = Graph().addEdge('a', 'b');
|
||||
assert.equal(graph.adjacent('a').length, 1);
|
||||
assert.equal(graph.adjacent('a')[0], 'b');
|
||||
});
|
||||
|
||||
it("Should remove edges.", function (){
|
||||
it('Should remove edges.', function() {
|
||||
var graph = Graph();
|
||||
graph.addEdge("a", "b");
|
||||
graph.removeEdge("a", "b");
|
||||
assert.equal(graph.adjacent("a").length, 0);
|
||||
graph.addEdge('a', 'b');
|
||||
graph.removeEdge('a', 'b');
|
||||
assert.equal(graph.adjacent('a').length, 0);
|
||||
});
|
||||
|
||||
it("Should chain removeEdge.", function (){
|
||||
it('Should chain removeEdge.', function() {
|
||||
var graph = Graph()
|
||||
.addEdge("a", "b")
|
||||
.removeEdge("a", "b");
|
||||
assert.equal(graph.adjacent("a").length, 0);
|
||||
.addEdge('a', 'b')
|
||||
.removeEdge('a', 'b');
|
||||
assert.equal(graph.adjacent('a').length, 0);
|
||||
});
|
||||
|
||||
it("Should not remove nodes when edges are removed.", function (){
|
||||
it('Should not remove nodes when edges are removed.', function() {
|
||||
var graph = Graph();
|
||||
graph.addEdge("a", "b");
|
||||
graph.removeEdge("a", "b");
|
||||
graph.addEdge('a', 'b');
|
||||
graph.removeEdge('a', 'b');
|
||||
assert.equal(graph.nodes().length, 2);
|
||||
assert(contains(graph.nodes(), "a"));
|
||||
assert(contains(graph.nodes(), "b"));
|
||||
assert(contains(graph.nodes(), 'a'));
|
||||
assert(contains(graph.nodes(), 'b'));
|
||||
});
|
||||
|
||||
it("Should remove outgoing edges when a node is removed.", function (){
|
||||
it('Should remove outgoing edges when a node is removed.', function() {
|
||||
var graph = Graph();
|
||||
graph.addEdge("a", "b");
|
||||
graph.removeNode("a");
|
||||
assert.equal(graph.adjacent("a").length, 0);
|
||||
graph.addEdge('a', 'b');
|
||||
graph.removeNode('a');
|
||||
assert.equal(graph.adjacent('a').length, 0);
|
||||
});
|
||||
|
||||
it("Should remove incoming edges when a node is removed.", function (){
|
||||
it('Should remove incoming edges when a node is removed.', function() {
|
||||
var graph = Graph();
|
||||
graph.addEdge("a", "b");
|
||||
graph.removeNode("b");
|
||||
assert.equal(graph.adjacent("a").length, 0);
|
||||
graph.addEdge('a', 'b');
|
||||
graph.removeNode('b');
|
||||
assert.equal(graph.adjacent('a').length, 0);
|
||||
});
|
||||
|
||||
it("Should compute indegree.", function (){
|
||||
it('Should compute indegree.', function() {
|
||||
var graph = Graph();
|
||||
graph.addEdge("a", "b");
|
||||
assert.equal(graph.indegree("a"), 0);
|
||||
assert.equal(graph.indegree("b"), 1);
|
||||
graph.addEdge('a', 'b');
|
||||
assert.equal(graph.indegree('a'), 0);
|
||||
assert.equal(graph.indegree('b'), 1);
|
||||
|
||||
graph.addEdge("c", "b");
|
||||
assert.equal(graph.indegree("b"), 2);
|
||||
graph.addEdge('c', 'b');
|
||||
assert.equal(graph.indegree('b'), 2);
|
||||
});
|
||||
|
||||
it("Should compute outdegree.", function (){
|
||||
it('Should compute outdegree.', function() {
|
||||
var graph = Graph();
|
||||
graph.addEdge("a", "b");
|
||||
assert.equal(graph.outdegree("a"), 1);
|
||||
assert.equal(graph.outdegree("b"), 0);
|
||||
graph.addEdge('a', 'b');
|
||||
assert.equal(graph.outdegree('a'), 1);
|
||||
assert.equal(graph.outdegree('b'), 0);
|
||||
|
||||
graph.addEdge("a", "c");
|
||||
assert.equal(graph.outdegree("a"), 2);
|
||||
graph.addEdge('a', 'c');
|
||||
assert.equal(graph.outdegree('a'), 2);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe("Algorithms", function() {
|
||||
|
||||
describe('Algorithms', function() {
|
||||
// This example is from Cormen et al. "Introduction to Algorithms" page 550
|
||||
it("Should compute topological sort.", function (){
|
||||
|
||||
it('Should compute topological sort.', function() {
|
||||
var graph = Graph();
|
||||
|
||||
// Shoes depend on socks.
|
||||
// Socks need to be put on before shoes.
|
||||
graph.addEdge("socks", "shoes");
|
||||
graph.addEdge('socks', 'shoes');
|
||||
|
||||
graph.addEdge("shirt", "belt");
|
||||
graph.addEdge("shirt", "tie");
|
||||
graph.addEdge("tie", "jacket");
|
||||
graph.addEdge("belt", "jacket");
|
||||
graph.addEdge("pants", "shoes");
|
||||
graph.addEdge("underpants", "pants");
|
||||
graph.addEdge("pants", "belt");
|
||||
graph.addEdge('shirt', 'belt');
|
||||
graph.addEdge('shirt', 'tie');
|
||||
graph.addEdge('tie', 'jacket');
|
||||
graph.addEdge('belt', 'jacket');
|
||||
graph.addEdge('pants', 'shoes');
|
||||
graph.addEdge('underpants', 'pants');
|
||||
graph.addEdge('pants', 'belt');
|
||||
|
||||
var sorted = graph.topologicalSort();
|
||||
|
||||
assert(comesBefore(sorted, "pants", "shoes"));
|
||||
assert(comesBefore(sorted, "underpants", "pants"));
|
||||
assert(comesBefore(sorted, "underpants", "shoes"));
|
||||
assert(comesBefore(sorted, "shirt", "jacket"));
|
||||
assert(comesBefore(sorted, "shirt", "belt"));
|
||||
assert(comesBefore(sorted, "belt", "jacket"));
|
||||
assert(comesBefore(sorted, 'pants', 'shoes'));
|
||||
assert(comesBefore(sorted, 'underpants', 'pants'));
|
||||
assert(comesBefore(sorted, 'underpants', 'shoes'));
|
||||
assert(comesBefore(sorted, 'shirt', 'jacket'));
|
||||
assert(comesBefore(sorted, 'shirt', 'belt'));
|
||||
assert(comesBefore(sorted, 'belt', 'jacket'));
|
||||
|
||||
assert.equal(sorted.length, 8);
|
||||
|
||||
});
|
||||
|
||||
it("Should compute topological sort, excluding source nodes.", function (){
|
||||
it('Should compute topological sort, excluding source nodes.', function() {
|
||||
var graph = Graph();
|
||||
graph.addEdge("a", "b");
|
||||
graph.addEdge("b", "c");
|
||||
var sorted = graph.topologicalSort(["a"], false);
|
||||
graph.addEdge('a', 'b');
|
||||
graph.addEdge('b', 'c');
|
||||
var sorted = graph.topologicalSort(['a'], false);
|
||||
assert.equal(sorted.length, 2);
|
||||
assert.equal(sorted[0], "b");
|
||||
assert.equal(sorted[1], "c");
|
||||
assert.equal(sorted[0], 'b');
|
||||
assert.equal(sorted[1], 'c');
|
||||
});
|
||||
|
||||
it("Should compute topological sort tricky case.", function (){
|
||||
|
||||
it('Should compute topological sort tricky case.', function() {
|
||||
var graph = Graph(); // a
|
||||
// / \
|
||||
graph.addEdge("a", "b"); // b |
|
||||
graph.addEdge("a", "d"); // | d
|
||||
graph.addEdge("b", "c"); // c |
|
||||
graph.addEdge("d", "e"); // \ /
|
||||
graph.addEdge("c", "e"); // e
|
||||
graph.addEdge('a', 'b'); // b |
|
||||
graph.addEdge('a', 'd'); // | d
|
||||
graph.addEdge('b', 'c'); // c |
|
||||
graph.addEdge('d', 'e'); // \ /
|
||||
graph.addEdge('c', 'e'); // e
|
||||
|
||||
var sorted = graph.topologicalSort(["a"], false);
|
||||
var sorted = graph.topologicalSort(['a'], false);
|
||||
assert.equal(sorted.length, 4);
|
||||
assert(contains(sorted, "b"));
|
||||
assert(contains(sorted, "c"));
|
||||
assert(contains(sorted, "d"));
|
||||
assert.equal(sorted[sorted.length - 1], "e");
|
||||
|
||||
assert(comesBefore(sorted, "b", "c"));
|
||||
assert(comesBefore(sorted, "b", "e"));
|
||||
assert(comesBefore(sorted, "c", "e"));
|
||||
assert(comesBefore(sorted, "d", "e"));
|
||||
assert(contains(sorted, 'b'));
|
||||
assert(contains(sorted, 'c'));
|
||||
assert(contains(sorted, 'd'));
|
||||
assert.equal(sorted[sorted.length - 1], 'e');
|
||||
|
||||
assert(comesBefore(sorted, 'b', 'c'));
|
||||
assert(comesBefore(sorted, 'b', 'e'));
|
||||
assert(comesBefore(sorted, 'c', 'e'));
|
||||
assert(comesBefore(sorted, 'd', 'e'));
|
||||
});
|
||||
|
||||
it("Should exclude source nodes with a cycle.", function (){
|
||||
it('Should exclude source nodes with a cycle.', function() {
|
||||
var graph = Graph()
|
||||
.addEdge("a", "b")
|
||||
.addEdge("b", "c")
|
||||
.addEdge("c", "a");
|
||||
var sorted = graph.topologicalSort(["a"], false);
|
||||
.addEdge('a', 'b')
|
||||
.addEdge('b', 'c')
|
||||
.addEdge('c', 'a');
|
||||
var sorted = graph.topologicalSort(['a'], false);
|
||||
assert.equal(sorted.length, 2);
|
||||
assert.equal(sorted[0], "b");
|
||||
assert.equal(sorted[1], "c");
|
||||
|
||||
assert.equal(sorted[0], 'b');
|
||||
assert.equal(sorted[1], 'c');
|
||||
});
|
||||
|
||||
it("Should exclude source nodes with multiple cycles.", function (){
|
||||
it('Should exclude source nodes with multiple cycles.', function() {
|
||||
var graph = Graph()
|
||||
.addEdge('a', 'b')
|
||||
.addEdge('b', 'a')
|
||||
|
||||
.addEdge("a", "b")
|
||||
.addEdge("b", "a")
|
||||
.addEdge('b', 'c')
|
||||
.addEdge('c', 'b')
|
||||
|
||||
.addEdge("b", "c")
|
||||
.addEdge("c", "b")
|
||||
.addEdge('a', 'c')
|
||||
.addEdge('c', 'a');
|
||||
|
||||
.addEdge("a", "c")
|
||||
.addEdge("c", "a");
|
||||
|
||||
var sorted = graph.topologicalSort(["a", "b"], false);
|
||||
assert(!contains(sorted, "b"));
|
||||
var sorted = graph.topologicalSort(['a', 'b'], false);
|
||||
assert(!contains(sorted, 'b'));
|
||||
});
|
||||
});
|
||||
|
||||
describe("Edge cases and error handling", function() {
|
||||
|
||||
it("Should return empty array of adjacent nodes for unknown nodes.", function (){
|
||||
describe('Edge cases and error handling', function() {
|
||||
it('Should return empty array of adjacent nodes for unknown nodes.', function() {
|
||||
var graph = Graph();
|
||||
assert.equal(graph.adjacent("a").length, 0);
|
||||
assert.equal(graph.adjacent('a').length, 0);
|
||||
assert.equal(graph.nodes(), 0);
|
||||
});
|
||||
|
||||
it("Should do nothing if removing an edge that does not exist.", function (){
|
||||
assert.doesNotThrow(function (){
|
||||
it('Should do nothing if removing an edge that does not exist.', function() {
|
||||
assert.doesNotThrow(function() {
|
||||
var graph = Graph();
|
||||
graph.removeEdge("a", "b");
|
||||
graph.removeEdge('a', 'b');
|
||||
});
|
||||
});
|
||||
|
||||
it("Should return indegree of 0 for unknown nodes.", function (){
|
||||
it('Should return indegree of 0 for unknown nodes.', function() {
|
||||
var graph = Graph();
|
||||
assert.equal(graph.indegree("z"), 0);
|
||||
assert.equal(graph.indegree('z'), 0);
|
||||
});
|
||||
|
||||
it("Should return outdegree of 0 for unknown nodes.", function (){
|
||||
it('Should return outdegree of 0 for unknown nodes.', function() {
|
||||
var graph = Graph();
|
||||
assert.equal(graph.outdegree("z"), 0);
|
||||
assert.equal(graph.outdegree('z'), 0);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe("Serialization", function() {
|
||||
|
||||
describe('Serialization', function() {
|
||||
var serialized;
|
||||
|
||||
function checkSerialized(graph){
|
||||
function checkSerialized(graph) {
|
||||
assert.equal(graph.nodes.length, 3);
|
||||
assert.equal(graph.links.length, 2);
|
||||
|
||||
assert.equal(graph.nodes[0].id, "a");
|
||||
assert.equal(graph.nodes[1].id, "b");
|
||||
assert.equal(graph.nodes[2].id, "c");
|
||||
assert.equal(graph.nodes[0].id, 'a');
|
||||
assert.equal(graph.nodes[1].id, 'b');
|
||||
assert.equal(graph.nodes[2].id, 'c');
|
||||
|
||||
assert.equal(graph.links[0].source, "a");
|
||||
assert.equal(graph.links[0].target, "b");
|
||||
assert.equal(graph.links[1].source, "b");
|
||||
assert.equal(graph.links[1].target, "c");
|
||||
assert.equal(graph.links[0].source, 'a');
|
||||
assert.equal(graph.links[0].target, 'b');
|
||||
assert.equal(graph.links[1].source, 'b');
|
||||
assert.equal(graph.links[1].target, 'c');
|
||||
}
|
||||
|
||||
it("Should serialize a graph.", function (){
|
||||
it('Should serialize a graph.', function() {
|
||||
var graph = Graph()
|
||||
.addEdge("a", "b")
|
||||
.addEdge("b", "c");
|
||||
.addEdge('a', 'b')
|
||||
.addEdge('b', 'c');
|
||||
serialized = graph.serialize();
|
||||
checkSerialized(serialized);
|
||||
});
|
||||
|
||||
it("Should deserialize a graph.", function (){
|
||||
it('Should deserialize a graph.', function() {
|
||||
var graph = Graph();
|
||||
graph.deserialize(serialized);
|
||||
checkSerialized(graph.serialize());
|
||||
});
|
||||
|
||||
it("Should chain deserialize a graph.", function (){
|
||||
it('Should chain deserialize a graph.', function() {
|
||||
var graph = Graph().deserialize(serialized);
|
||||
checkSerialized(graph.serialize());
|
||||
});
|
||||
|
||||
it("Should deserialize a graph passed to constructor.", function (){
|
||||
it('Should deserialize a graph passed to constructor.', function() {
|
||||
var graph = Graph(serialized);
|
||||
checkSerialized(graph.serialize());
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function contains(arr, item){
|
||||
return arr.filter(function (d){
|
||||
function contains(arr, item) {
|
||||
return (
|
||||
arr.filter(function(d) {
|
||||
return d === item;
|
||||
}).length > 0;
|
||||
}).length > 0
|
||||
);
|
||||
}
|
||||
|
||||
function comesBefore(arr, a, b){
|
||||
function comesBefore(arr, a, b) {
|
||||
var aIndex, bIndex;
|
||||
arr.forEach(function (d, i){
|
||||
if(d === a){ aIndex = i; }
|
||||
if(d === b){ bIndex = i; }
|
||||
arr.forEach(function(d, i) {
|
||||
if (d === a) {
|
||||
aIndex = i;
|
||||
}
|
||||
if (d === b) {
|
||||
bIndex = i;
|
||||
}
|
||||
});
|
||||
return aIndex < bIndex;
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
const sqlite = require("sqlite3");
|
||||
const escodegen = require("escodegen");
|
||||
const sqlgen = require("./sqlgen");
|
||||
const sqlite = require('sqlite3');
|
||||
const escodegen = require('escodegen');
|
||||
const sqlgen = require('./sqlgen');
|
||||
|
||||
// Example usage:
|
||||
|
||||
const Spreadsheet = require("./spreadsheet");
|
||||
const Spreadsheet = require('./spreadsheet');
|
||||
|
||||
const db = new sqlite.Database(__dirname + "/../../db.sqlite");
|
||||
const db = new sqlite.Database(__dirname + '/../../db.sqlite');
|
||||
const sheet = new Spreadsheet({
|
||||
plugins: {
|
||||
runQuery: {
|
||||
|
@ -20,22 +20,24 @@ const sheet = new Spreadsheet({
|
|||
|
||||
return {
|
||||
data: {
|
||||
type: "query",
|
||||
type: 'query',
|
||||
query: query,
|
||||
sql: sql
|
||||
},
|
||||
|
||||
ast: {
|
||||
type: "CallExpression",
|
||||
type: 'CallExpression',
|
||||
callee: {
|
||||
type: "Identifier",
|
||||
name: "runQuery"
|
||||
type: 'Identifier',
|
||||
name: 'runQuery'
|
||||
},
|
||||
arguments: [{
|
||||
type: "Literal",
|
||||
arguments: [
|
||||
{
|
||||
type: 'Literal',
|
||||
raw: sql,
|
||||
value: sql
|
||||
}]
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
},
|
||||
|
@ -43,7 +45,7 @@ const sheet = new Spreadsheet({
|
|||
return new Promise(resolve => {
|
||||
const start = Date.now();
|
||||
db.all(sql, function(err, rows) {
|
||||
if(err) {
|
||||
if (err) {
|
||||
throw new Error(err);
|
||||
}
|
||||
resolve(rows);
|
||||
|
@ -54,11 +56,12 @@ const sheet = new Spreadsheet({
|
|||
}
|
||||
});
|
||||
|
||||
db.on("preupdate", function(type, dbname, table, old, _new, oldId, newId) {
|
||||
db.on('preupdate', function(type, dbname, table, old, _new, oldId, newId) {
|
||||
sheet.resolve().then(() => {
|
||||
const start = Date.now();
|
||||
sheet.startTransaction();
|
||||
sheet.getNodesOfType("query")
|
||||
sheet
|
||||
.getNodesOfType('query')
|
||||
.filter(node => node.data.query.table === table)
|
||||
.forEach(q => {
|
||||
sheet.signal(q.name);
|
||||
|
|
|
@ -2,13 +2,13 @@ import * as prefs from '../prefs';
|
|||
import * as db from '../db';
|
||||
import * as sheet from '../sheet';
|
||||
import * as sync from './index';
|
||||
import { getClock, Timestamp } from '../crdt';
|
||||
import { merkle } from '../crdt';
|
||||
import { merkle, getClock, Timestamp } from '../crdt';
|
||||
import * as encoder from './encoder';
|
||||
const jsc = require('jsverify');
|
||||
const uuidGenerator = jsc
|
||||
.integer(97, 122)
|
||||
.smap(x => String.fromCharCode(x), x => x.charCodeAt(x));
|
||||
const uuidGenerator = jsc.integer(97, 122).smap(
|
||||
x => String.fromCharCode(x),
|
||||
x => x.charCodeAt(x)
|
||||
);
|
||||
|
||||
const mockSyncServer = require('../tests/mockSyncServer');
|
||||
|
||||
|
@ -126,7 +126,10 @@ Object.keys(schema).forEach(table => {
|
|||
generators.push(
|
||||
makeGen({
|
||||
table,
|
||||
row: jsc.asciinestring.smap(x => 'sheet!' + x, x => x),
|
||||
row: jsc.asciinestring.smap(
|
||||
x => 'sheet!' + x,
|
||||
x => x
|
||||
),
|
||||
field: 'expr',
|
||||
value: jsc.constant(JSON.stringify('fooooo'))
|
||||
})
|
||||
|
|
|
@ -24,11 +24,9 @@ function isAnonymous(id) {
|
|||
return !id.startsWith('user-');
|
||||
}
|
||||
|
||||
export async function init() {
|
||||
}
|
||||
export async function init() {}
|
||||
|
||||
export async function login(userId) {
|
||||
}
|
||||
export async function login(userId) {}
|
||||
|
||||
let BUFFERING = false;
|
||||
let BUFFER = [];
|
||||
|
|
77
yarn.lock
77
yarn.lock
|
@ -2503,13 +2503,13 @@ __metadata:
|
|||
linkType: hard
|
||||
|
||||
"@babel/types@npm:^7.8.3":
|
||||
version: 7.18.10
|
||||
resolution: "@babel/types@npm:7.18.10"
|
||||
version: 7.18.13
|
||||
resolution: "@babel/types@npm:7.18.13"
|
||||
dependencies:
|
||||
"@babel/helper-string-parser": ^7.18.10
|
||||
"@babel/helper-validator-identifier": ^7.18.6
|
||||
to-fast-properties: ^2.0.0
|
||||
checksum: 11632c9b106e54021937a6498138014ebc9ad6c327a07b2af3ba8700773945aba4055fd136431cbe3a500d0f363cbf9c68eb4d6d38229897c5de9d06e14c85e8
|
||||
checksum: abc3ad1f3b6864df0ea0e778bcdf7d2c5ee2293811192962d50e8a8c05c1aeec90a48275f53b2a45aad882ed8bef9477ae1f8e70ac1d44d039e14930d1388dcc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
@ -3104,22 +3104,22 @@ __metadata:
|
|||
linkType: hard
|
||||
|
||||
"@npmcli/fs@npm:^2.1.0":
|
||||
version: 2.1.1
|
||||
resolution: "@npmcli/fs@npm:2.1.1"
|
||||
version: 2.1.2
|
||||
resolution: "@npmcli/fs@npm:2.1.2"
|
||||
dependencies:
|
||||
"@gar/promisify": ^1.1.3
|
||||
semver: ^7.3.5
|
||||
checksum: 4944a0545d38d3e6e29780eeb3cd4be6059c1e9627509d2c9ced635c53b852d28b37cdc615a2adf815b51ab8673adb6507e370401a20a7e90c8a6dc4fac02389
|
||||
checksum: 405074965e72d4c9d728931b64d2d38e6ea12066d4fad651ac253d175e413c06fe4350970c783db0d749181da8fe49c42d3880bd1cbc12cd68e3a7964d820225
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@npmcli/move-file@npm:^2.0.0":
|
||||
version: 2.0.0
|
||||
resolution: "@npmcli/move-file@npm:2.0.0"
|
||||
version: 2.0.1
|
||||
resolution: "@npmcli/move-file@npm:2.0.1"
|
||||
dependencies:
|
||||
mkdirp: ^1.0.4
|
||||
rimraf: ^3.0.2
|
||||
checksum: 1388777b507b0c592d53f41b9d182e1a8de7763bc625fc07999b8edbc22325f074e5b3ec90af79c89d6987fdb2325bc66d59f483258543c14a43661621f841b0
|
||||
checksum: 52dc02259d98da517fae4cb3a0a3850227bdae4939dda1980b788a7670636ca2b4a01b58df03dd5f65c1e3cb70c50fa8ce5762b582b3f499ec30ee5ce1fd9380
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
@ -6826,8 +6826,8 @@ __metadata:
|
|||
linkType: hard
|
||||
|
||||
"cacache@npm:^16.1.0":
|
||||
version: 16.1.1
|
||||
resolution: "cacache@npm:16.1.1"
|
||||
version: 16.1.2
|
||||
resolution: "cacache@npm:16.1.2"
|
||||
dependencies:
|
||||
"@npmcli/fs": ^2.1.0
|
||||
"@npmcli/move-file": ^2.0.0
|
||||
|
@ -6847,7 +6847,7 @@ __metadata:
|
|||
ssri: ^9.0.0
|
||||
tar: ^6.1.11
|
||||
unique-filename: ^1.1.1
|
||||
checksum: 488524617008b793f0249b0c4ea2c330c710ca997921376e15650cc2415a8054491ae2dee9f01382c2015602c0641f3f977faf2fa7361aa33d2637dcfb03907a
|
||||
checksum: defe1d6f557ddda178204cac111990da27e8a60ed276fcd608dad7109cc1936e7dcd57d7263d22cdb06a80e7ceb76ab5eb05133c7c7f886abf1d870d722abd6c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
@ -10175,6 +10175,21 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"eslint-plugin-prettier@npm:^3.1.4":
|
||||
version: 3.4.1
|
||||
resolution: "eslint-plugin-prettier@npm:3.4.1"
|
||||
dependencies:
|
||||
prettier-linter-helpers: ^1.0.0
|
||||
peerDependencies:
|
||||
eslint: ">=5.0.0"
|
||||
prettier: ">=1.13.0"
|
||||
peerDependenciesMeta:
|
||||
eslint-config-prettier:
|
||||
optional: true
|
||||
checksum: fa6a89f0d7cba1cc87064352f5a4a68dc3739448dd279bec2bced1bfa3b704467e603d13b69dcec853f8fa30b286b8b715912898e9da776e1b016cf0ee48bd99
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"eslint-plugin-react@npm:7.11.1":
|
||||
version: 7.11.1
|
||||
resolution: "eslint-plugin-react@npm:7.11.1"
|
||||
|
@ -10707,6 +10722,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"fast-diff@npm:^1.1.2":
|
||||
version: 1.2.0
|
||||
resolution: "fast-diff@npm:1.2.0"
|
||||
checksum: 1b5306eaa9e826564d9e5ffcd6ebd881eb5f770b3f977fcbf38f05c824e42172b53c79920e8429c54eb742ce15a0caf268b0fdd5b38f6de52234c4a8368131ae
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"fast-glob@npm:^2.2.0, fast-glob@npm:^2.2.2":
|
||||
version: 2.2.7
|
||||
resolution: "fast-glob@npm:2.2.7"
|
||||
|
@ -15303,6 +15325,7 @@ jest-snapshot@test:
|
|||
date-fns: 2.0.0-alpha.27
|
||||
deep-equal: ^2.0.5
|
||||
eslint: 5.6.0
|
||||
eslint-plugin-prettier: ^3.1.4
|
||||
esm: ^3.0.82
|
||||
fake-indexeddb: ^3.1.3
|
||||
fast-check: ^2.11.0
|
||||
|
@ -15321,6 +15344,7 @@ jest-snapshot@test:
|
|||
node-fetch: ^1.6.3
|
||||
node-libofx: "*"
|
||||
perf-deets: ^1.0.15
|
||||
prettier: ^1.19.1
|
||||
regenerator-runtime: ^0.13.7
|
||||
sanitize-filename: ^1.6.1
|
||||
search-query-parser: ^1.3.0
|
||||
|
@ -15433,9 +15457,9 @@ jest-snapshot@test:
|
|||
linkType: hard
|
||||
|
||||
"lru-cache@npm:^7.7.1":
|
||||
version: 7.13.2
|
||||
resolution: "lru-cache@npm:7.13.2"
|
||||
checksum: dfed24e52bae95edf490d0f28f4f14552319ac7e7dc37ae0b84a72e084949233821b33227271abe81d8361ac079810f9d171a706f316cfdeda135012e4311015
|
||||
version: 7.14.0
|
||||
resolution: "lru-cache@npm:7.14.0"
|
||||
checksum: efdd329f2c1bb790b71d497c6c59272e6bc2d7dd060ba55fc136becd3dd31fc8346edb446275504d94cb60d3c8385dbf5267b79b23789e409b2bdf302d13f0d7
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
@ -15516,8 +15540,8 @@ jest-snapshot@test:
|
|||
linkType: hard
|
||||
|
||||
"make-fetch-happen@npm:^10.0.3":
|
||||
version: 10.2.0
|
||||
resolution: "make-fetch-happen@npm:10.2.0"
|
||||
version: 10.2.1
|
||||
resolution: "make-fetch-happen@npm:10.2.1"
|
||||
dependencies:
|
||||
agentkeepalive: ^4.2.1
|
||||
cacache: ^16.1.0
|
||||
|
@ -15535,7 +15559,7 @@ jest-snapshot@test:
|
|||
promise-retry: ^2.0.1
|
||||
socks-proxy-agent: ^7.0.0
|
||||
ssri: ^9.0.0
|
||||
checksum: 2f6c294179972f56fab40fd8618f07841e06550692bb78f6da16e7afaa9dca78c345b08cf44a77a8907ef3948e4dc77e93eb7492b8381f1217d7ac057a7522f8
|
||||
checksum: 2332eb9a8ec96f1ffeeea56ccefabcb4193693597b132cd110734d50f2928842e22b84cfa1508e921b8385cdfd06dda9ad68645fed62b50fff629a580f5fb72c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
@ -16249,8 +16273,8 @@ jest-snapshot@test:
|
|||
linkType: hard
|
||||
|
||||
"minipass-fetch@npm:^2.0.3":
|
||||
version: 2.1.0
|
||||
resolution: "minipass-fetch@npm:2.1.0"
|
||||
version: 2.1.2
|
||||
resolution: "minipass-fetch@npm:2.1.2"
|
||||
dependencies:
|
||||
encoding: ^0.1.13
|
||||
minipass: ^3.1.6
|
||||
|
@ -16259,7 +16283,7 @@ jest-snapshot@test:
|
|||
dependenciesMeta:
|
||||
encoding:
|
||||
optional: true
|
||||
checksum: 1334732859a3f7959ed22589bafd9c40384b885aebb5932328071c33f86b3eb181d54c86919675d1825ab5f1c8e4f328878c863873258d113c29d79a4b0c9c9f
|
||||
checksum: 3f216be79164e915fc91210cea1850e488793c740534985da017a4cbc7a5ff50506956d0f73bb0cb60e4fe91be08b6b61ef35101706d3ef5da2c8709b5f08f91
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
@ -19080,7 +19104,16 @@ jest-snapshot@test:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"prettier@npm:^1.14.2, prettier@npm:^1.18.1":
|
||||
"prettier-linter-helpers@npm:^1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "prettier-linter-helpers@npm:1.0.0"
|
||||
dependencies:
|
||||
fast-diff: ^1.1.2
|
||||
checksum: 00ce8011cf6430158d27f9c92cfea0a7699405633f7f1d4a45f07e21bf78e99895911cbcdc3853db3a824201a7c745bd49bfea8abd5fb9883e765a90f74f8392
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"prettier@npm:^1.14.2, prettier@npm:^1.18.1, prettier@npm:^1.19.1":
|
||||
version: 1.19.1
|
||||
resolution: "prettier@npm:1.19.1"
|
||||
bin:
|
||||
|
|
Loading…
Reference in a new issue