@laboratoria/sdk-js 0.1.1 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -3
- package/index.js +12 -20
- package/lib/core.js +66 -11
- package/lib/curriculum.js +59 -0
- package/lib/model.js +107 -41
- package/package.json +21 -11
- package/schemas/core.json +211 -11
- package/index.spec.js +0 -112
- package/lib/client.spec.js +0 -77
- package/lib/currencies.js +0 -3
- package/lib/model.spec.js +0 -215
- package/lib/team.js +0 -52
- package/schemas/team.json +0 -146
package/README.md
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
## Laboratoria JavaScript (browser) SDK
|
2
2
|
|
3
|
+
[](https://github.com/Laboratoria/sdk-js/actions/workflows/node.js.yml)
|
4
|
+
|
3
5
|
:warning: This tool is still in draft stage and is likely to change without
|
4
6
|
notice.
|
5
7
|
|
@@ -25,7 +27,6 @@ const app = createApp({
|
|
25
27
|
firebaseApiKey: '',
|
26
28
|
firebaseProject: '',
|
27
29
|
coreApiUrl: '',
|
28
|
-
teamApiUrl: '',
|
29
30
|
jobsApiUrl: '',
|
30
31
|
});
|
31
32
|
```
|
@@ -79,7 +80,6 @@ app.user.create({
|
|
79
80
|
### Cohorts
|
80
81
|
|
81
82
|
```js
|
82
|
-
// Cohorts API
|
83
83
|
app.cohort.findMany({
|
84
84
|
where: {
|
85
85
|
end: { gt: new Date() },
|
@@ -96,13 +96,50 @@ app.cohort.findMany({
|
|
96
96
|
|
97
97
|
### Students
|
98
98
|
|
99
|
+
WIP
|
100
|
+
|
99
101
|
### Dropouts
|
100
102
|
|
103
|
+
WIP
|
101
104
|
|
102
105
|
### Projects
|
103
106
|
|
104
107
|
```js
|
105
108
|
app.project.findMany();
|
106
109
|
|
107
|
-
|
110
|
+
app.project.findMany({ tag: 'v3.0.0' });
|
111
|
+
|
112
|
+
app.project.findMany({
|
113
|
+
locale: 'es-ES',
|
114
|
+
track: 'js',
|
115
|
+
});
|
116
|
+
|
117
|
+
app.project.findById('cipher');
|
118
|
+
|
119
|
+
app.project.findById('cipher', { tag: 'v3.0.0' });
|
108
120
|
```
|
121
|
+
|
122
|
+
### Topics
|
123
|
+
|
124
|
+
```js
|
125
|
+
app.topic.findMany();
|
126
|
+
|
127
|
+
app.topic.findMany({ tag: 'v3.0.0' });
|
128
|
+
|
129
|
+
app.topic.findMany({
|
130
|
+
locale: 'es-ES',
|
131
|
+
track: 'js',
|
132
|
+
});
|
133
|
+
|
134
|
+
app.topic.findById('javascript');
|
135
|
+
|
136
|
+
app.topic.findById('javascript', { tag: 'v3.0.0' });
|
137
|
+
```
|
138
|
+
|
139
|
+
### Learning Objectives
|
140
|
+
|
141
|
+
```js
|
142
|
+
app.learningObjective.findMany();
|
143
|
+
|
144
|
+
app.learningObjective.findMany({ tag: 'v3.0.0' });
|
145
|
+
```
|
package/index.js
CHANGED
@@ -5,16 +5,14 @@ import {
|
|
5
5
|
signInWithEmailAndPassword,
|
6
6
|
signOut,
|
7
7
|
} from 'firebase/auth';
|
8
|
-
import { createClient } from './lib/client';
|
9
8
|
import { createAPI as createCoreAPI } from './lib/core';
|
9
|
+
import { createAPI as createCurriculumAPI } from './lib/curriculum';
|
10
10
|
import { createAPI as createJobsAPI } from './lib/jobs';
|
11
|
-
import { createAPI as createTeamAPI } from './lib/team';
|
12
11
|
|
13
12
|
export const createApp = ({
|
14
13
|
firebaseApiKey = 'AIzaSyAXbaEbpq8NOfn0r8mIrcoHvoGRkJThwdc',
|
15
14
|
firebaseProject = 'laboratoria-la',
|
16
15
|
coreApiUrl = 'https://us-central1-outpost-272720.cloudfunctions.net/core-api',
|
17
|
-
teamApiUrl = 'https://us-central1-outpost-272720.cloudfunctions.net/team-api',
|
18
16
|
jobsApiUrl = 'https://us-central1-outpost-272720.cloudfunctions.net/jobs-api',
|
19
17
|
} = {}) => {
|
20
18
|
const firebaseApp = initializeApp({
|
@@ -26,31 +24,25 @@ export const createApp = ({
|
|
26
24
|
|
27
25
|
const firebaseAuth = getAuth(firebaseApp);
|
28
26
|
const state = { authUser: undefined, user: undefined };
|
29
|
-
|
30
|
-
const
|
31
|
-
|
32
|
-
};
|
27
|
+
const coreAPI = createCoreAPI(coreApiUrl, state);
|
28
|
+
const jobsAPI = createJobsAPI(jobsApiUrl, state);
|
29
|
+
const curriculumAPI = createCurriculumAPI();
|
33
30
|
|
34
31
|
const authAPI = {
|
35
32
|
onChange: fn => onAuthStateChanged(firebaseAuth, (authUser) => {
|
36
33
|
if (!authUser) {
|
37
|
-
|
34
|
+
Object.assign(state, { authUser: null, user: null });
|
38
35
|
return fn(state);
|
39
36
|
}
|
40
|
-
|
37
|
+
Object.assign(state, { authUser });
|
38
|
+
coreAPI.user.findById(authUser.uid)
|
41
39
|
.then((user) => {
|
42
|
-
Object.assign(
|
43
|
-
isStaff: ['staff', 'manager', 'finance', 'admin'].includes(user.role),
|
44
|
-
isManager: ['manager', 'finance', 'admin'].includes(user.role),
|
45
|
-
isFinance: ['finance', 'admin'].includes(user.role),
|
46
|
-
isAdmin: user.role === 'admin',
|
47
|
-
});
|
48
|
-
setState(authUser, user);
|
40
|
+
Object.assign(state, { user });
|
49
41
|
fn(state);
|
50
42
|
})
|
51
43
|
.catch((err) => {
|
52
44
|
console.error(err);
|
53
|
-
|
45
|
+
Object.assign(state, { authUser: null, user: null });
|
54
46
|
fn(state);
|
55
47
|
});
|
56
48
|
}),
|
@@ -63,9 +55,9 @@ export const createApp = ({
|
|
63
55
|
};
|
64
56
|
|
65
57
|
return {
|
58
|
+
...jobsAPI,
|
59
|
+
...curriculumAPI,
|
60
|
+
...coreAPI,
|
66
61
|
auth: authAPI,
|
67
|
-
...createJobsAPI(jobsApiUrl, state),
|
68
|
-
...createTeamAPI(teamApiUrl, state),
|
69
|
-
...createCoreAPI(coreApiUrl, state),
|
70
62
|
};
|
71
63
|
};
|
package/lib/core.js
CHANGED
@@ -1,14 +1,20 @@
|
|
1
|
+
import md5 from 'blueimp-md5';
|
1
2
|
import { createModels, extendSchemaDefinitions } from './model';
|
2
3
|
import schema from '../schemas/core.json';
|
3
4
|
|
4
5
|
const extended = {
|
6
|
+
Country: {
|
7
|
+
primaryKey: 'code',
|
8
|
+
plural: 'countries',
|
9
|
+
searchProps: ['code', 'name'],
|
10
|
+
},
|
5
11
|
User: {
|
6
12
|
primaryKey: 'uid',
|
7
13
|
inputProps: [
|
8
14
|
'firstName',
|
9
15
|
'lastName',
|
10
16
|
'email',
|
11
|
-
'
|
17
|
+
'lang',
|
12
18
|
'github',
|
13
19
|
'linkedin',
|
14
20
|
'bio',
|
@@ -22,17 +28,24 @@ const extended = {
|
|
22
28
|
email: {
|
23
29
|
inputType: 'email',
|
24
30
|
},
|
25
|
-
locale: {
|
26
|
-
enum: ['es-ES', 'pt-BR'],
|
27
|
-
},
|
28
31
|
bio: {
|
29
32
|
inputType: 'textarea',
|
30
33
|
},
|
31
34
|
},
|
32
35
|
parse: (props) => {
|
36
|
+
const avatar = (
|
37
|
+
props.github
|
38
|
+
? `https://github.com/${props.github}.png?size=`
|
39
|
+
: `https://www.gravatar.com/avatar/${md5(props.email)}?s=`
|
40
|
+
)
|
33
41
|
return {
|
34
42
|
...props,
|
35
43
|
fullName: `${props.firstName} ${props.lastName}`,
|
44
|
+
avatar: size => `${avatar}${size}`,
|
45
|
+
isStaff: ['staff', 'manager', 'finance', 'admin'].includes(props.role),
|
46
|
+
isManager: ['manager', 'finance', 'admin'].includes(props.role),
|
47
|
+
isFinance: ['finance', 'admin'].includes(props.role),
|
48
|
+
isAdmin: props.role === 'admin',
|
36
49
|
};
|
37
50
|
},
|
38
51
|
},
|
@@ -59,6 +72,27 @@ const extended = {
|
|
59
72
|
`${name}${!stage ? '' : ` - ${stage.name}`} (id: ${id})`
|
60
73
|
),
|
61
74
|
properties: {},
|
75
|
+
parse: (props) => {
|
76
|
+
const now = Date.now();
|
77
|
+
// TODO: Handle case where start and/or end have not been selected in query
|
78
|
+
const status = (
|
79
|
+
props.start > now
|
80
|
+
? 'upcoming'
|
81
|
+
: props.end < now
|
82
|
+
? 'past'
|
83
|
+
: 'active'
|
84
|
+
);
|
85
|
+
const size = (
|
86
|
+
status === 'upcoming' && props.vacancies
|
87
|
+
? props.vacancies
|
88
|
+
: props._count?.students || props.students?.length || 0
|
89
|
+
);
|
90
|
+
return {
|
91
|
+
...props,
|
92
|
+
status,
|
93
|
+
size,
|
94
|
+
};
|
95
|
+
},
|
62
96
|
},
|
63
97
|
Student: {
|
64
98
|
inputProps: [],
|
@@ -71,8 +105,8 @@ const extended = {
|
|
71
105
|
'date',
|
72
106
|
'reason',
|
73
107
|
'reasonDetail',
|
74
|
-
'
|
75
|
-
'
|
108
|
+
'lastProject',
|
109
|
+
'completedProjects',
|
76
110
|
'notes',
|
77
111
|
'staffSad',
|
78
112
|
'covidRelated',
|
@@ -85,14 +119,35 @@ const extended = {
|
|
85
119
|
'cohort.name',
|
86
120
|
],
|
87
121
|
},
|
122
|
+
Contract: {
|
123
|
+
inputProps: [
|
124
|
+
'user',
|
125
|
+
'country',
|
126
|
+
'currency',
|
127
|
+
'isEmployment',
|
128
|
+
'feeBasis',
|
129
|
+
'feeAmount',
|
130
|
+
'hoursPerWeek',
|
131
|
+
'start',
|
132
|
+
'end',
|
133
|
+
],
|
134
|
+
searchProps: ['user.firstName'],
|
135
|
+
getOptionLabel: ({ id, user }) => `${user?.firstName} (id: ${id})`,
|
136
|
+
},
|
137
|
+
Gig: {
|
138
|
+
inputProps: [
|
139
|
+
'cohort',
|
140
|
+
'user',
|
141
|
+
'contract',
|
142
|
+
'role',
|
143
|
+
'hoursPerWeek',
|
144
|
+
'start',
|
145
|
+
'end',
|
146
|
+
],
|
147
|
+
},
|
88
148
|
};
|
89
149
|
|
90
150
|
export const createAPI = (url, state) => createModels(url, state, {
|
91
151
|
...schema,
|
92
152
|
definitions: extendSchemaDefinitions(schema, extended),
|
93
153
|
});
|
94
|
-
|
95
|
-
// const {
|
96
|
-
// delete: _,
|
97
|
-
// ...userAPI,
|
98
|
-
// } = createModel(coreApiUrl, state, 'users', schemas.user);
|
@@ -0,0 +1,59 @@
|
|
1
|
+
const releasesUrl = 'https://api.github.com/repos/Laboratoria/bootcamp/releases';
|
2
|
+
const rawUrl = 'https://raw.githubusercontent.com/Laboratoria/bootcamp';
|
3
|
+
|
4
|
+
const getLatestVersion = () => fetch(`${releasesUrl}/latest`)
|
5
|
+
.then(resp => resp.json())
|
6
|
+
.then(({ tag_name }) => tag_name);
|
7
|
+
|
8
|
+
const fetchProjects = async tag => (
|
9
|
+
fetch(`${rawUrl}/${tag || await getLatestVersion()}/dist/projects.json`)
|
10
|
+
.then(resp => resp.json())
|
11
|
+
);
|
12
|
+
|
13
|
+
const fetchProject = async (slug, tag) => (
|
14
|
+
fetch(`${rawUrl}/${tag || await getLatestVersion()}/dist/projects/${slug}.json`)
|
15
|
+
.then(resp => resp.json())
|
16
|
+
);
|
17
|
+
|
18
|
+
const fetchTopics = async tag => (
|
19
|
+
fetch(`${rawUrl}/${tag || await getLatestVersion()}/dist/topics.json`)
|
20
|
+
.then(resp => resp.json())
|
21
|
+
);
|
22
|
+
|
23
|
+
const fetchTopic = async (slug, tag) => (
|
24
|
+
fetch(`${rawUrl}/${tag || await getLatestVersion()}/dist/topics/${slug}.json`)
|
25
|
+
.then(resp => resp.json())
|
26
|
+
);
|
27
|
+
|
28
|
+
const fetchLearningObjectives = async tag => (
|
29
|
+
fetch(`${rawUrl}/${tag || await getLatestVersion()}/dist/learning-objectives.json`)
|
30
|
+
.then(resp => resp.json())
|
31
|
+
);
|
32
|
+
|
33
|
+
const filterByLocaleAndTrack = opts => arr => arr.filter((obj) => {
|
34
|
+
if (opts.locale && opts.locale !== obj.locale) {
|
35
|
+
return false;
|
36
|
+
}
|
37
|
+
if (opts.track && opts.track !== obj.track) {
|
38
|
+
return false;
|
39
|
+
}
|
40
|
+
return true;
|
41
|
+
});
|
42
|
+
|
43
|
+
export const createAPI = () => {
|
44
|
+
return {
|
45
|
+
projects: {
|
46
|
+
findMany: (opts = {}) => fetchProjects(opts.tag)
|
47
|
+
.then(filterByLocaleAndTrack(opts)),
|
48
|
+
findById: (slug, opts = {}) => fetchProject(slug, opts.tag),
|
49
|
+
},
|
50
|
+
topics: {
|
51
|
+
findMany: (opts = {}) => fetchTopics(opts.tag)
|
52
|
+
.then(filterByLocaleAndTrack(opts)),
|
53
|
+
findById: (slug, opts = {}) => fetchTopic(slug, opts.tag),
|
54
|
+
},
|
55
|
+
learningObjectives: {
|
56
|
+
findMany: (opts = {}) => fetchLearningObjectives(opts.tag),
|
57
|
+
},
|
58
|
+
};
|
59
|
+
};
|
package/lib/model.js
CHANGED
@@ -1,13 +1,53 @@
|
|
1
1
|
import { createClient } from './client.js';
|
2
2
|
|
3
|
+
|
4
|
+
const isRequiredOneToOneRelation = (schema, key) => (
|
5
|
+
!!schema.properties
|
6
|
+
&& !!schema.properties[key]
|
7
|
+
&& !!schema.properties[key].$ref
|
8
|
+
);
|
9
|
+
|
10
|
+
const isOptionalOneToOneRelation = (schema, key) => (
|
11
|
+
!!schema.properties
|
12
|
+
&& !!schema.properties[key]
|
13
|
+
&& Array.isArray(schema.properties[key].anyOf)
|
14
|
+
&& !!schema.properties[key].anyOf[0]?.$ref
|
15
|
+
&& schema.properties[key].anyOf[1]?.type === 'null'
|
16
|
+
);
|
17
|
+
|
18
|
+
const isOneToOneRelation = (schema, key) => (
|
19
|
+
isRequiredOneToOneRelation(schema, key)
|
20
|
+
|| isOptionalOneToOneRelation(schema, key)
|
21
|
+
);
|
22
|
+
|
23
|
+
const isOneToManyRelation = (schema, key) => (
|
24
|
+
schema.properties
|
25
|
+
&& schema.properties[key]
|
26
|
+
&& schema.properties[key].type === 'array'
|
27
|
+
&& !!schema.properties[key].items.$ref
|
28
|
+
);
|
29
|
+
|
30
|
+
// const isRelation = (schema, key) => (
|
31
|
+
// isOneToOneRelation(schema, key)
|
32
|
+
// || isOneToManyRelation(schema, key)
|
33
|
+
// );
|
34
|
+
|
35
|
+
|
3
36
|
const createValidator = (schema) => {
|
4
37
|
const properties = schema.properties || {};
|
5
38
|
const inputProps = schema.inputProps || Object.keys(properties);
|
6
39
|
|
7
40
|
const validateAttr = (key, value) => {
|
8
41
|
const attrSchema = properties[key] || {};
|
9
|
-
const type =
|
10
|
-
|
42
|
+
const type = (
|
43
|
+
Array.isArray(attrSchema.type)
|
44
|
+
? attrSchema.type[0]
|
45
|
+
: attrSchema.type
|
46
|
+
);
|
47
|
+
const isRequired = (
|
48
|
+
!Array.isArray(attrSchema.type)
|
49
|
+
|| !attrSchema.type.includes('null')
|
50
|
+
);
|
11
51
|
|
12
52
|
if (attrSchema.enum) {
|
13
53
|
if (!isRequired && !value) {
|
@@ -63,43 +103,42 @@ const createBuildURL = collectionName => (id, q) => {
|
|
63
103
|
};
|
64
104
|
|
65
105
|
|
66
|
-
const
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
{},
|
85
|
-
);
|
106
|
+
const serializeData = (data, schema) => {
|
107
|
+
const hasInputProps = Array.isArray(schema.inputProps);
|
108
|
+
return Object.keys(data).reduce(
|
109
|
+
(memo, key) => {
|
110
|
+
if (hasInputProps && !schema.inputProps.includes(key)) {
|
111
|
+
return memo;
|
112
|
+
}
|
113
|
+
if (isOptionalOneToOneRelation(schema, key) && data[key] === null) {
|
114
|
+
return memo;
|
115
|
+
}
|
116
|
+
return {
|
117
|
+
...memo,
|
118
|
+
[key]: data[key],
|
119
|
+
};
|
120
|
+
},
|
121
|
+
{},
|
122
|
+
);
|
123
|
+
};
|
86
124
|
|
87
125
|
|
88
|
-
const createModel = (baseUrl, state, collectionName, schema = {}) => {
|
126
|
+
export const createModel = (baseUrl, state, collectionName, schema = {}) => {
|
127
|
+
const primaryKey = schema.primaryKey || 'id';
|
89
128
|
const validator = createValidator(schema);
|
90
129
|
const buildURL = createBuildURL(collectionName);
|
91
|
-
const defaultInclude = buildDefaultInclude(schema);
|
92
|
-
const buildQuery = createBuildQuery(defaultInclude);
|
93
130
|
const req = (...args) => createClient(baseUrl, state.authUser)(...args);
|
94
|
-
const create = q => req(buildURL(), {
|
131
|
+
const create = ({ data, ...q }) => req(buildURL(null, q), {
|
95
132
|
method: 'POST',
|
96
|
-
body:
|
97
|
-
});
|
98
|
-
|
99
|
-
const put = q => req(buildURL(q.where.id), {
|
100
|
-
method: 'PUT',
|
101
|
-
body: buildQuery(q),
|
133
|
+
body: serializeData(data, schema),
|
102
134
|
});
|
135
|
+
const put = ({ data, ...q }) => {
|
136
|
+
const { where, ...rest } = q;
|
137
|
+
return req(buildURL(where[primaryKey], rest), {
|
138
|
+
method: 'PUT',
|
139
|
+
body: serializeData(data, schema),
|
140
|
+
});
|
141
|
+
};
|
103
142
|
|
104
143
|
const parse = data => {
|
105
144
|
const parsed = Object.keys(data).reduce(
|
@@ -123,23 +162,53 @@ const createModel = (baseUrl, state, collectionName, schema = {}) => {
|
|
123
162
|
);
|
124
163
|
};
|
125
164
|
|
165
|
+
const relations = Object.keys(schema.properties || {}).reduce(
|
166
|
+
(memo, key) => (
|
167
|
+
isRequiredOneToOneRelation(schema, key)
|
168
|
+
? Object.assign(memo, {
|
169
|
+
all: memo.all.concat(key),
|
170
|
+
oneToOne: memo.oneToOne.concat(key),
|
171
|
+
requiredOneToOne: memo.requiredOneToOne.concat(key),
|
172
|
+
})
|
173
|
+
: isOptionalOneToOneRelation(schema, key)
|
174
|
+
? Object.assign(memo, {
|
175
|
+
all: memo.all.concat(key),
|
176
|
+
oneToOne: memo.oneToOne.concat(key),
|
177
|
+
optionalOneToOne: memo.optionalOneToOne.concat(key),
|
178
|
+
})
|
179
|
+
: isOneToManyRelation(schema, key)
|
180
|
+
? Object.assign(memo, {
|
181
|
+
all: memo.all.concat(key),
|
182
|
+
oneToMany: memo.oneToMany.concat(key),
|
183
|
+
})
|
184
|
+
: memo
|
185
|
+
),
|
186
|
+
{
|
187
|
+
all: [],
|
188
|
+
oneToOne: [],
|
189
|
+
requiredOneToOne: [],
|
190
|
+
optionalOneToOne: [],
|
191
|
+
oneToMany: [],
|
192
|
+
},
|
193
|
+
);
|
194
|
+
|
126
195
|
return {
|
127
196
|
get schema() {
|
128
|
-
return schema;
|
197
|
+
return { ...schema, primaryKey };
|
129
198
|
},
|
130
|
-
get
|
131
|
-
return
|
199
|
+
get relations() {
|
200
|
+
return relations;
|
132
201
|
},
|
133
202
|
validateAttr: validator.validateAttr,
|
134
203
|
validate: validator.validate,
|
135
|
-
findMany: q => req(buildURL(null,
|
204
|
+
findMany: q => req(buildURL(null, q))
|
136
205
|
.then(results => results.map(parse)),
|
137
|
-
findById: (id, q) => req(buildURL(id,
|
206
|
+
findById: (id, q) => req(buildURL(id, q))
|
138
207
|
.then(raw => !!raw ? parse(raw) : raw),
|
139
208
|
create,
|
140
209
|
update: put,
|
141
210
|
upsert: opts => (
|
142
|
-
!opts.where
|
211
|
+
!opts.where[primaryKey]
|
143
212
|
? create({ data: opts.create })
|
144
213
|
: put({ where: opts.where, data: opts.update })
|
145
214
|
),
|
@@ -147,9 +216,6 @@ const createModel = (baseUrl, state, collectionName, schema = {}) => {
|
|
147
216
|
};
|
148
217
|
};
|
149
218
|
|
150
|
-
|
151
|
-
export default createModel;
|
152
|
-
|
153
219
|
export const createModels = (url, state, schema) => {
|
154
220
|
return Object.keys(schema.properties).reduce(
|
155
221
|
(prev, key) => {
|
package/package.json
CHANGED
@@ -1,23 +1,33 @@
|
|
1
1
|
{
|
2
2
|
"name": "@laboratoria/sdk-js",
|
3
|
-
"version": "
|
3
|
+
"version": "1.2.0",
|
4
4
|
"description": "Laboratoria JavaScript (browser) SDK",
|
5
5
|
"scripts": {
|
6
|
-
"test": "jest --verbose --coverage"
|
6
|
+
"test": "jest --verbose --coverage",
|
7
|
+
"changelog": "git log $(git describe --tags --abbrev=0)..HEAD --oneline --format=\"* %h %s (%an)\""
|
7
8
|
},
|
8
9
|
"license": "MIT",
|
9
10
|
"dependencies": {
|
10
|
-
"
|
11
|
+
"blueimp-md5": "^2.19.0",
|
12
|
+
"firebase": "^9.6.5"
|
11
13
|
},
|
12
14
|
"devDependencies": {
|
13
|
-
"@babel/core": "^7.
|
14
|
-
"@babel/plugin-transform-modules-commonjs": "^7.16.
|
15
|
-
"babel-jest": "^27.4.
|
16
|
-
"jest": "^27.4.
|
17
|
-
"webpack": "^5.
|
18
|
-
"webpack-cli": "^4.9.
|
15
|
+
"@babel/core": "^7.17.0",
|
16
|
+
"@babel/plugin-transform-modules-commonjs": "^7.16.8",
|
17
|
+
"babel-jest": "^27.4.6",
|
18
|
+
"jest": "^27.4.7",
|
19
|
+
"webpack": "^5.68.0",
|
20
|
+
"webpack-cli": "^4.9.2"
|
19
21
|
},
|
20
22
|
"jest": {
|
21
|
-
"testEnvironment": "jsdom"
|
23
|
+
"testEnvironment": "jsdom",
|
24
|
+
"coverageThreshold": {
|
25
|
+
"global": {
|
26
|
+
"statements": 97,
|
27
|
+
"branches": 92,
|
28
|
+
"functions": 98,
|
29
|
+
"lines": 97
|
30
|
+
}
|
31
|
+
}
|
22
32
|
}
|
23
|
-
}
|
33
|
+
}
|