Showing
2 changed files
with
21 additions
and
307 deletions
application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java
deleted
100644 → 0
1 | -/** | ||
2 | - * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | - * | ||
4 | - * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | - * you may not use this file except in compliance with the License. | ||
6 | - * You may obtain a copy of the License at | ||
7 | - * | ||
8 | - * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | - * | ||
10 | - * Unless required by applicable law or agreed to in writing, software | ||
11 | - * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | - * See the License for the specific language governing permissions and | ||
14 | - * limitations under the License. | ||
15 | - */ | ||
16 | -package org.thingsboard.server.service.install; | ||
17 | - | ||
18 | -import com.datastax.driver.core.KeyspaceMetadata; | ||
19 | -import com.datastax.driver.core.exceptions.InvalidQueryException; | ||
20 | -import lombok.extern.slf4j.Slf4j; | ||
21 | -import org.springframework.beans.factory.annotation.Autowired; | ||
22 | -import org.springframework.context.annotation.Profile; | ||
23 | -import org.springframework.stereotype.Service; | ||
24 | -import org.thingsboard.server.dao.dashboard.DashboardService; | ||
25 | -import org.thingsboard.server.dao.util.NoSqlDao; | ||
26 | -import org.thingsboard.server.service.install.cql.CassandraDbHelper; | ||
27 | - | ||
28 | -import java.nio.file.Files; | ||
29 | -import java.nio.file.Path; | ||
30 | -import java.nio.file.Paths; | ||
31 | - | ||
32 | -import static org.thingsboard.server.service.install.DatabaseHelper.ADDITIONAL_INFO; | ||
33 | -import static org.thingsboard.server.service.install.DatabaseHelper.ASSET; | ||
34 | -import static org.thingsboard.server.service.install.DatabaseHelper.ASSIGNED_CUSTOMERS; | ||
35 | -import static org.thingsboard.server.service.install.DatabaseHelper.CONFIGURATION; | ||
36 | -import static org.thingsboard.server.service.install.DatabaseHelper.CUSTOMER_ID; | ||
37 | -import static org.thingsboard.server.service.install.DatabaseHelper.DASHBOARD; | ||
38 | -import static org.thingsboard.server.service.install.DatabaseHelper.DEVICE; | ||
39 | -import static org.thingsboard.server.service.install.DatabaseHelper.END_TS; | ||
40 | -import static org.thingsboard.server.service.install.DatabaseHelper.ENTITY_ID; | ||
41 | -import static org.thingsboard.server.service.install.DatabaseHelper.ENTITY_TYPE; | ||
42 | -import static org.thingsboard.server.service.install.DatabaseHelper.ENTITY_VIEW; | ||
43 | -import static org.thingsboard.server.service.install.DatabaseHelper.ENTITY_VIEWS; | ||
44 | -import static org.thingsboard.server.service.install.DatabaseHelper.ID; | ||
45 | -import static org.thingsboard.server.service.install.DatabaseHelper.KEYS; | ||
46 | -import static org.thingsboard.server.service.install.DatabaseHelper.NAME; | ||
47 | -import static org.thingsboard.server.service.install.DatabaseHelper.SEARCH_TEXT; | ||
48 | -import static org.thingsboard.server.service.install.DatabaseHelper.START_TS; | ||
49 | -import static org.thingsboard.server.service.install.DatabaseHelper.TENANT_ID; | ||
50 | -import static org.thingsboard.server.service.install.DatabaseHelper.TITLE; | ||
51 | -import static org.thingsboard.server.service.install.DatabaseHelper.TYPE; | ||
52 | - | ||
53 | -@Service | ||
54 | -@NoSqlDao | ||
55 | -@Profile("install") | ||
56 | -@Slf4j | ||
57 | -public class CassandraDatabaseUpgradeService extends AbstractCassandraDatabaseUpgradeService implements DatabaseEntitiesUpgradeService { | ||
58 | - | ||
59 | - private static final String SCHEMA_UPDATE_CQL = "schema_update.cql"; | ||
60 | - | ||
61 | - @Autowired | ||
62 | - private DashboardService dashboardService; | ||
63 | - | ||
64 | - @Autowired | ||
65 | - private InstallScripts installScripts; | ||
66 | - | ||
67 | - @Override | ||
68 | - public void upgradeDatabase(String fromVersion) throws Exception { | ||
69 | - | ||
70 | - switch (fromVersion) { | ||
71 | - case "1.2.3": | ||
72 | - | ||
73 | - log.info("Upgrading Cassandara DataBase from version {} to 1.3.0 ...", fromVersion); | ||
74 | - | ||
75 | - //Dump devices, assets and relations | ||
76 | - | ||
77 | - cluster.getSession(); | ||
78 | - | ||
79 | - KeyspaceMetadata ks = cluster.getCluster().getMetadata().getKeyspace(cluster.getKeyspaceName()); | ||
80 | - | ||
81 | - log.info("Dumping devices ..."); | ||
82 | - Path devicesDump = CassandraDbHelper.dumpCfIfExists(ks, cluster.getSession(), DEVICE, | ||
83 | - new String[]{"id", TENANT_ID, CUSTOMER_ID, "name", SEARCH_TEXT, ADDITIONAL_INFO, "type"}, | ||
84 | - new String[]{"", "", "", "", "", "", "default"}, | ||
85 | - "tb-devices"); | ||
86 | - log.info("Devices dumped."); | ||
87 | - | ||
88 | - log.info("Dumping assets ..."); | ||
89 | - Path assetsDump = CassandraDbHelper.dumpCfIfExists(ks, cluster.getSession(), ASSET, | ||
90 | - new String[]{"id", TENANT_ID, CUSTOMER_ID, "name", SEARCH_TEXT, ADDITIONAL_INFO, "type"}, | ||
91 | - new String[]{"", "", "", "", "", "", "default"}, | ||
92 | - "tb-assets"); | ||
93 | - log.info("Assets dumped."); | ||
94 | - | ||
95 | - log.info("Dumping relations ..."); | ||
96 | - Path relationsDump = CassandraDbHelper.dumpCfIfExists(ks, cluster.getSession(), "relation", | ||
97 | - new String[]{"from_id", "from_type", "to_id", "to_type", "relation_type", ADDITIONAL_INFO, "relation_type_group"}, | ||
98 | - new String[]{"", "", "", "", "", "", "COMMON"}, | ||
99 | - "tb-relations"); | ||
100 | - log.info("Relations dumped."); | ||
101 | - | ||
102 | - log.info("Updating schema ..."); | ||
103 | - Path schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "1.3.0", SCHEMA_UPDATE_CQL); | ||
104 | - loadCql(schemaUpdateFile); | ||
105 | - log.info("Schema updated."); | ||
106 | - | ||
107 | - //Restore devices, assets and relations | ||
108 | - | ||
109 | - log.info("Restoring devices ..."); | ||
110 | - if (devicesDump != null) { | ||
111 | - CassandraDbHelper.loadCf(ks, cluster.getSession(), DEVICE, | ||
112 | - new String[]{"id", TENANT_ID, CUSTOMER_ID, "name", SEARCH_TEXT, ADDITIONAL_INFO, "type"}, devicesDump); | ||
113 | - Files.deleteIfExists(devicesDump); | ||
114 | - } | ||
115 | - log.info("Devices restored."); | ||
116 | - | ||
117 | - log.info("Dumping device types ..."); | ||
118 | - Path deviceTypesDump = CassandraDbHelper.dumpCfIfExists(ks, cluster.getSession(), DEVICE, | ||
119 | - new String[]{TENANT_ID, "type"}, | ||
120 | - new String[]{"", ""}, | ||
121 | - "tb-device-types"); | ||
122 | - if (deviceTypesDump != null) { | ||
123 | - CassandraDbHelper.appendToEndOfLine(deviceTypesDump, "DEVICE"); | ||
124 | - } | ||
125 | - log.info("Device types dumped."); | ||
126 | - log.info("Loading device types ..."); | ||
127 | - if (deviceTypesDump != null) { | ||
128 | - CassandraDbHelper.loadCf(ks, cluster.getSession(), "entity_subtype", | ||
129 | - new String[]{TENANT_ID, "type", "entity_type"}, deviceTypesDump); | ||
130 | - Files.deleteIfExists(deviceTypesDump); | ||
131 | - } | ||
132 | - log.info("Device types loaded."); | ||
133 | - | ||
134 | - log.info("Restoring assets ..."); | ||
135 | - if (assetsDump != null) { | ||
136 | - CassandraDbHelper.loadCf(ks, cluster.getSession(), ASSET, | ||
137 | - new String[]{"id", TENANT_ID, CUSTOMER_ID, "name", SEARCH_TEXT, ADDITIONAL_INFO, "type"}, assetsDump); | ||
138 | - Files.deleteIfExists(assetsDump); | ||
139 | - } | ||
140 | - log.info("Assets restored."); | ||
141 | - | ||
142 | - log.info("Dumping asset types ..."); | ||
143 | - Path assetTypesDump = CassandraDbHelper.dumpCfIfExists(ks, cluster.getSession(), ASSET, | ||
144 | - new String[]{TENANT_ID, "type"}, | ||
145 | - new String[]{"", ""}, | ||
146 | - "tb-asset-types"); | ||
147 | - if (assetTypesDump != null) { | ||
148 | - CassandraDbHelper.appendToEndOfLine(assetTypesDump, "ASSET"); | ||
149 | - } | ||
150 | - log.info("Asset types dumped."); | ||
151 | - log.info("Loading asset types ..."); | ||
152 | - if (assetTypesDump != null) { | ||
153 | - CassandraDbHelper.loadCf(ks, cluster.getSession(), "entity_subtype", | ||
154 | - new String[]{TENANT_ID, "type", "entity_type"}, assetTypesDump); | ||
155 | - Files.deleteIfExists(assetTypesDump); | ||
156 | - } | ||
157 | - log.info("Asset types loaded."); | ||
158 | - | ||
159 | - log.info("Restoring relations ..."); | ||
160 | - if (relationsDump != null) { | ||
161 | - CassandraDbHelper.loadCf(ks, cluster.getSession(), "relation", | ||
162 | - new String[]{"from_id", "from_type", "to_id", "to_type", "relation_type", ADDITIONAL_INFO, "relation_type_group"}, relationsDump); | ||
163 | - Files.deleteIfExists(relationsDump); | ||
164 | - } | ||
165 | - log.info("Relations restored."); | ||
166 | - | ||
167 | - break; | ||
168 | - case "1.3.0": | ||
169 | - break; | ||
170 | - case "1.3.1": | ||
171 | - | ||
172 | - cluster.getSession(); | ||
173 | - | ||
174 | - ks = cluster.getCluster().getMetadata().getKeyspace(cluster.getKeyspaceName()); | ||
175 | - | ||
176 | - log.info("Dumping dashboards ..."); | ||
177 | - Path dashboardsDump = CassandraDbHelper.dumpCfIfExists(ks, cluster.getSession(), DASHBOARD, | ||
178 | - new String[]{ID, TENANT_ID, CUSTOMER_ID, TITLE, SEARCH_TEXT, ASSIGNED_CUSTOMERS, CONFIGURATION}, | ||
179 | - new String[]{"", "", "", "", "", "", ""}, | ||
180 | - "tb-dashboards", true); | ||
181 | - log.info("Dashboards dumped."); | ||
182 | - | ||
183 | - | ||
184 | - log.info("Updating schema ..."); | ||
185 | - schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "1.4.0", SCHEMA_UPDATE_CQL); | ||
186 | - loadCql(schemaUpdateFile); | ||
187 | - log.info("Schema updated."); | ||
188 | - | ||
189 | - log.info("Restoring dashboards ..."); | ||
190 | - if (dashboardsDump != null) { | ||
191 | - CassandraDbHelper.loadCf(ks, cluster.getSession(), DASHBOARD, | ||
192 | - new String[]{ID, TENANT_ID, TITLE, SEARCH_TEXT, CONFIGURATION}, dashboardsDump, true); | ||
193 | - DatabaseHelper.upgradeTo40_assignDashboards(dashboardsDump, dashboardService, false); | ||
194 | - Files.deleteIfExists(dashboardsDump); | ||
195 | - } | ||
196 | - log.info("Dashboards restored."); | ||
197 | - break; | ||
198 | - case "1.4.0": | ||
199 | - | ||
200 | - log.info("Updating schema ..."); | ||
201 | - schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "2.0.0", SCHEMA_UPDATE_CQL); | ||
202 | - loadCql(schemaUpdateFile); | ||
203 | - log.info("Schema updated."); | ||
204 | - | ||
205 | - break; | ||
206 | - | ||
207 | - case "2.0.0": | ||
208 | - | ||
209 | - log.info("Updating schema ..."); | ||
210 | - schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "2.1.1", SCHEMA_UPDATE_CQL); | ||
211 | - loadCql(schemaUpdateFile); | ||
212 | - log.info("Schema updated."); | ||
213 | - | ||
214 | - break; | ||
215 | - | ||
216 | - case "2.1.1": | ||
217 | - | ||
218 | - log.info("Upgrading Cassandra DataBase from version {} to 2.1.2 ...", fromVersion); | ||
219 | - | ||
220 | - cluster.getSession(); | ||
221 | - | ||
222 | - ks = cluster.getCluster().getMetadata().getKeyspace(cluster.getKeyspaceName()); | ||
223 | - | ||
224 | - log.info("Dumping entity views ..."); | ||
225 | - Path entityViewsDump = CassandraDbHelper.dumpCfIfExists(ks, cluster.getSession(), ENTITY_VIEWS, | ||
226 | - new String[]{ID, ENTITY_ID, ENTITY_TYPE, TENANT_ID, CUSTOMER_ID, NAME, TYPE, KEYS, START_TS, END_TS, SEARCH_TEXT, ADDITIONAL_INFO}, | ||
227 | - new String[]{"", "", "", "", "", "", "default", "", "0", "0", "", ""}, | ||
228 | - "tb-entity-views"); | ||
229 | - log.info("Entity views dumped."); | ||
230 | - | ||
231 | - log.info("Updating schema ..."); | ||
232 | - schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "2.1.2", SCHEMA_UPDATE_CQL); | ||
233 | - loadCql(schemaUpdateFile); | ||
234 | - log.info("Schema updated."); | ||
235 | - | ||
236 | - log.info("Restoring entity views ..."); | ||
237 | - if (entityViewsDump != null) { | ||
238 | - CassandraDbHelper.loadCf(ks, cluster.getSession(), ENTITY_VIEW, | ||
239 | - new String[]{ID, ENTITY_ID, ENTITY_TYPE, TENANT_ID, CUSTOMER_ID, NAME, TYPE, KEYS, START_TS, END_TS, SEARCH_TEXT, ADDITIONAL_INFO}, entityViewsDump); | ||
240 | - Files.deleteIfExists(entityViewsDump); | ||
241 | - } | ||
242 | - log.info("Entity views restored."); | ||
243 | - | ||
244 | - break; | ||
245 | - case "2.1.3": | ||
246 | - break; | ||
247 | - case "2.3.0": | ||
248 | - break; | ||
249 | - case "2.3.1": | ||
250 | - log.info("Updating schema ..."); | ||
251 | - String updateDeviceTableStmt = "alter table device add label text"; | ||
252 | - try { | ||
253 | - cluster.getSession().execute(updateDeviceTableStmt); | ||
254 | - Thread.sleep(2500); | ||
255 | - } catch (InvalidQueryException e) { | ||
256 | - } | ||
257 | - log.info("Schema updated."); | ||
258 | - break; | ||
259 | - case "2.4.1": | ||
260 | - log.info("Updating schema ..."); | ||
261 | - String updateAssetTableStmt = "alter table asset add label text"; | ||
262 | - try { | ||
263 | - log.info("Updating assets ..."); | ||
264 | - cluster.getSession().execute(updateAssetTableStmt); | ||
265 | - Thread.sleep(2500); | ||
266 | - log.info("Assets updated."); | ||
267 | - } catch (InvalidQueryException e) { | ||
268 | - } | ||
269 | - log.info("Schema updated."); | ||
270 | - break; | ||
271 | - case "2.4.2": | ||
272 | - log.info("Updating schema ..."); | ||
273 | - String updateAlarmTableStmt = "alter table alarm add propagate_relation_types text"; | ||
274 | - try { | ||
275 | - log.info("Updating alarms ..."); | ||
276 | - cluster.getSession().execute(updateAlarmTableStmt); | ||
277 | - Thread.sleep(2500); | ||
278 | - log.info("Alarms updated."); | ||
279 | - } catch (InvalidQueryException e) { | ||
280 | - } | ||
281 | - log.info("Schema updated."); | ||
282 | - break; | ||
283 | - case "2.4.3": | ||
284 | - log.info("Updating schema ..."); | ||
285 | - String updateAttributeKvTableStmt = "alter table attributes_kv_cf add json_v text"; | ||
286 | - try { | ||
287 | - log.info("Updating attributes ..."); | ||
288 | - cluster.getSession().execute(updateAttributeKvTableStmt); | ||
289 | - Thread.sleep(2500); | ||
290 | - log.info("Attributes updated."); | ||
291 | - } catch (InvalidQueryException e) { | ||
292 | - } | ||
293 | - log.info("Schema updated."); | ||
294 | - break; | ||
295 | - default: | ||
296 | - throw new RuntimeException("Unable to upgrade Cassandra database, unsupported fromVersion: " + fromVersion); | ||
297 | - } | ||
298 | - } | ||
299 | - | ||
300 | -} |
@@ -1226,12 +1226,14 @@ | @@ -1226,12 +1226,14 @@ | ||
1226 | "balanced-match": { | 1226 | "balanced-match": { |
1227 | "version": "1.0.0", | 1227 | "version": "1.0.0", |
1228 | "bundled": true, | 1228 | "bundled": true, |
1229 | - "dev": true | 1229 | + "dev": true, |
1230 | + "optional": true | ||
1230 | }, | 1231 | }, |
1231 | "brace-expansion": { | 1232 | "brace-expansion": { |
1232 | "version": "1.1.11", | 1233 | "version": "1.1.11", |
1233 | "bundled": true, | 1234 | "bundled": true, |
1234 | "dev": true, | 1235 | "dev": true, |
1236 | + "optional": true, | ||
1235 | "requires": { | 1237 | "requires": { |
1236 | "balanced-match": "^1.0.0", | 1238 | "balanced-match": "^1.0.0", |
1237 | "concat-map": "0.0.1" | 1239 | "concat-map": "0.0.1" |
@@ -1246,17 +1248,20 @@ | @@ -1246,17 +1248,20 @@ | ||
1246 | "code-point-at": { | 1248 | "code-point-at": { |
1247 | "version": "1.1.0", | 1249 | "version": "1.1.0", |
1248 | "bundled": true, | 1250 | "bundled": true, |
1249 | - "dev": true | 1251 | + "dev": true, |
1252 | + "optional": true | ||
1250 | }, | 1253 | }, |
1251 | "concat-map": { | 1254 | "concat-map": { |
1252 | "version": "0.0.1", | 1255 | "version": "0.0.1", |
1253 | "bundled": true, | 1256 | "bundled": true, |
1254 | - "dev": true | 1257 | + "dev": true, |
1258 | + "optional": true | ||
1255 | }, | 1259 | }, |
1256 | "console-control-strings": { | 1260 | "console-control-strings": { |
1257 | "version": "1.1.0", | 1261 | "version": "1.1.0", |
1258 | "bundled": true, | 1262 | "bundled": true, |
1259 | - "dev": true | 1263 | + "dev": true, |
1264 | + "optional": true | ||
1260 | }, | 1265 | }, |
1261 | "core-util-is": { | 1266 | "core-util-is": { |
1262 | "version": "1.0.2", | 1267 | "version": "1.0.2", |
@@ -1373,7 +1378,8 @@ | @@ -1373,7 +1378,8 @@ | ||
1373 | "inherits": { | 1378 | "inherits": { |
1374 | "version": "2.0.4", | 1379 | "version": "2.0.4", |
1375 | "bundled": true, | 1380 | "bundled": true, |
1376 | - "dev": true | 1381 | + "dev": true, |
1382 | + "optional": true | ||
1377 | }, | 1383 | }, |
1378 | "ini": { | 1384 | "ini": { |
1379 | "version": "1.3.5", | 1385 | "version": "1.3.5", |
@@ -1385,6 +1391,7 @@ | @@ -1385,6 +1391,7 @@ | ||
1385 | "version": "1.0.0", | 1391 | "version": "1.0.0", |
1386 | "bundled": true, | 1392 | "bundled": true, |
1387 | "dev": true, | 1393 | "dev": true, |
1394 | + "optional": true, | ||
1388 | "requires": { | 1395 | "requires": { |
1389 | "number-is-nan": "^1.0.0" | 1396 | "number-is-nan": "^1.0.0" |
1390 | } | 1397 | } |
@@ -1399,6 +1406,7 @@ | @@ -1399,6 +1406,7 @@ | ||
1399 | "version": "3.0.4", | 1406 | "version": "3.0.4", |
1400 | "bundled": true, | 1407 | "bundled": true, |
1401 | "dev": true, | 1408 | "dev": true, |
1409 | + "optional": true, | ||
1402 | "requires": { | 1410 | "requires": { |
1403 | "brace-expansion": "^1.1.7" | 1411 | "brace-expansion": "^1.1.7" |
1404 | } | 1412 | } |
@@ -1406,12 +1414,14 @@ | @@ -1406,12 +1414,14 @@ | ||
1406 | "minimist": { | 1414 | "minimist": { |
1407 | "version": "0.0.8", | 1415 | "version": "0.0.8", |
1408 | "bundled": true, | 1416 | "bundled": true, |
1409 | - "dev": true | 1417 | + "dev": true, |
1418 | + "optional": true | ||
1410 | }, | 1419 | }, |
1411 | "minipass": { | 1420 | "minipass": { |
1412 | "version": "2.9.0", | 1421 | "version": "2.9.0", |
1413 | "bundled": true, | 1422 | "bundled": true, |
1414 | "dev": true, | 1423 | "dev": true, |
1424 | + "optional": true, | ||
1415 | "requires": { | 1425 | "requires": { |
1416 | "safe-buffer": "^5.1.2", | 1426 | "safe-buffer": "^5.1.2", |
1417 | "yallist": "^3.0.0" | 1427 | "yallist": "^3.0.0" |
@@ -1430,6 +1440,7 @@ | @@ -1430,6 +1440,7 @@ | ||
1430 | "version": "0.5.1", | 1440 | "version": "0.5.1", |
1431 | "bundled": true, | 1441 | "bundled": true, |
1432 | "dev": true, | 1442 | "dev": true, |
1443 | + "optional": true, | ||
1433 | "requires": { | 1444 | "requires": { |
1434 | "minimist": "0.0.8" | 1445 | "minimist": "0.0.8" |
1435 | } | 1446 | } |
@@ -1519,7 +1530,8 @@ | @@ -1519,7 +1530,8 @@ | ||
1519 | "number-is-nan": { | 1530 | "number-is-nan": { |
1520 | "version": "1.0.1", | 1531 | "version": "1.0.1", |
1521 | "bundled": true, | 1532 | "bundled": true, |
1522 | - "dev": true | 1533 | + "dev": true, |
1534 | + "optional": true | ||
1523 | }, | 1535 | }, |
1524 | "object-assign": { | 1536 | "object-assign": { |
1525 | "version": "4.1.1", | 1537 | "version": "4.1.1", |
@@ -1531,6 +1543,7 @@ | @@ -1531,6 +1543,7 @@ | ||
1531 | "version": "1.4.0", | 1543 | "version": "1.4.0", |
1532 | "bundled": true, | 1544 | "bundled": true, |
1533 | "dev": true, | 1545 | "dev": true, |
1546 | + "optional": true, | ||
1534 | "requires": { | 1547 | "requires": { |
1535 | "wrappy": "1" | 1548 | "wrappy": "1" |
1536 | } | 1549 | } |
@@ -1652,6 +1665,7 @@ | @@ -1652,6 +1665,7 @@ | ||
1652 | "version": "1.0.2", | 1665 | "version": "1.0.2", |
1653 | "bundled": true, | 1666 | "bundled": true, |
1654 | "dev": true, | 1667 | "dev": true, |
1668 | + "optional": true, | ||
1655 | "requires": { | 1669 | "requires": { |
1656 | "code-point-at": "^1.0.0", | 1670 | "code-point-at": "^1.0.0", |
1657 | "is-fullwidth-code-point": "^1.0.0", | 1671 | "is-fullwidth-code-point": "^1.0.0", |