@eluvio/elv-client-js 3.1.92 → 3.1.95

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.
Files changed (35) hide show
  1. package/dist/ElvClient-min.js +15 -11
  2. package/dist/ElvClient-node-min.js +17 -13
  3. package/dist/ElvFrameClient-min.js +13 -9
  4. package/dist/ElvPermissionsClient-min.js +13 -9
  5. package/dist/src/AuthorizationClient.js +2248 -1990
  6. package/dist/src/ContentObjectVerification.js +164 -173
  7. package/dist/src/Crypto.js +376 -324
  8. package/dist/src/ElvClient.js +1214 -938
  9. package/dist/src/ElvWallet.js +119 -95
  10. package/dist/src/EthClient.js +1040 -896
  11. package/dist/src/FrameClient.js +331 -300
  12. package/dist/src/HttpClient.js +153 -147
  13. package/dist/src/Id.js +1 -3
  14. package/dist/src/PermissionsClient.js +1294 -1168
  15. package/dist/src/RemoteSigner.js +260 -200
  16. package/dist/src/UserProfileClient.js +1168 -1014
  17. package/dist/src/Utils.js +382 -178
  18. package/dist/src/client/ABRPublishing.js +895 -856
  19. package/dist/src/client/AccessGroups.js +1102 -959
  20. package/dist/src/client/ContentAccess.js +3737 -3427
  21. package/dist/src/client/ContentManagement.js +2255 -2063
  22. package/dist/src/client/Contracts.js +647 -563
  23. package/dist/src/client/Files.js +1886 -1757
  24. package/dist/src/client/NFT.js +127 -158
  25. package/dist/src/client/NTP.js +478 -422
  26. package/package.json +2 -1
  27. package/src/ElvClient.js +62 -1
  28. package/src/RemoteSigner.js +22 -18
  29. package/src/Utils.js +119 -0
  30. package/src/client/ABRPublishing.js +2 -1
  31. package/src/client/NFT.js +14 -49
  32. package/testScripts/Test.js +0 -19
  33. package/testScripts/VariantABRProfile.js +45 -0
  34. package/utilities/SimpleIngest.js +245 -0
  35. package/package-lock.json +0 -12650
package/dist/src/Utils.js CHANGED
@@ -2,13 +2,17 @@ var _toConsumableArray = require("@babel/runtime/helpers/toConsumableArray");
2
2
 
3
3
  var _regeneratorRuntime = require("@babel/runtime/regenerator");
4
4
 
5
+ var _asyncToGenerator = require("@babel/runtime/helpers/asyncToGenerator");
6
+
5
7
  var _defineProperty = require("@babel/runtime/helpers/defineProperty");
6
8
 
7
9
  var _slicedToArray = require("@babel/runtime/helpers/slicedToArray");
8
10
 
9
- function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
11
+ var _this = this;
12
+
13
+ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
10
14
 
11
- function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
15
+ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
12
16
 
13
17
  if (typeof Buffer === "undefined") {
14
18
  Buffer = require("buffer/").Buffer;
@@ -22,7 +26,9 @@ var VarInt = require("varint");
22
26
 
23
27
  var URI = require("urijs");
24
28
 
25
- var keccak256 = require("ethers").utils.keccak256;
29
+ var _require$utils = require("ethers").utils,
30
+ keccak256 = _require$utils.keccak256,
31
+ getAddress = _require$utils.getAddress;
26
32
  /**
27
33
  * @namespace
28
34
  * @description This is a utility namespace mostly containing functions for managing
@@ -161,6 +167,145 @@ var Utils = {
161
167
  };
162
168
  },
163
169
 
170
+ /**
171
+ * Decode the specified write token into its component parts
172
+ *
173
+ * @param writeToken
174
+ *
175
+ * @returns {Object} - Components of the write token.
176
+ */
177
+ DecodeWriteToken: function DecodeWriteToken(writeToken) {
178
+ /*
179
+ Format:
180
+ - content write token, LRO:
181
+ - prefix: "tq__", "tqw__", "tlro"
182
+ - format:
183
+ prefix + base58(uvarint(len(QID) | QID |
184
+ uvarint(len(NID) | NID |
185
+ uvarint(len(RAND_BYTES) | RAND_BYTES)
186
+ - content part write token:
187
+ - prefix: "tqp_"
188
+ - format:
189
+ prefix + base58(scheme | flags | uvarint(len(RAND_BYTES) | RAND_BYTES)
190
+ - content write token v1, content part write token v1:
191
+ - prefix: "tqw_", "tqpw"
192
+ - format:
193
+ prefix + base58(RAND_BYTES)
194
+ */
195
+ if (writeToken.length < 4) {
196
+ throw new Error("Invalid write token: [\"".concat(writeToken, "\"] (unknown prefix)"));
197
+ }
198
+
199
+ var tokenType;
200
+
201
+ if (writeToken.startsWith("tqw__")) {
202
+ tokenType = "tq__";
203
+ writeToken = writeToken.slice(5);
204
+ } else {
205
+ tokenType = writeToken.slice(0, 4);
206
+ writeToken = writeToken.slice(4);
207
+ }
208
+
209
+ if (writeToken.length === 0) {
210
+ throw new Error("Invalid write token: [\"".concat(writeToken, "\"] (too short)"));
211
+ }
212
+
213
+ switch (tokenType) {
214
+ case "tqw_":
215
+ case "tq__":
216
+ case "tqpw":
217
+ case "tqp_":
218
+ case "tlro":
219
+ break;
220
+
221
+ default:
222
+ throw new Error("Invalid write token: [\"".concat(writeToken, "\"] (unknown prefix)"));
223
+ } // decode base58 payload
224
+
225
+
226
+ var bytes = Utils.FromB58(writeToken);
227
+
228
+ function decodeBytes(isID, prefix) {
229
+ var bsize = VarInt.decode(bytes, 0); // decode: count of bytes to read
230
+
231
+ var offset = VarInt.decode.bytes; // offset in buffer to start read after decode
232
+
233
+ var theBytes;
234
+ var ret;
235
+
236
+ if (isID) {
237
+ theBytes = bytes.slice(offset + 1, bsize + offset); // skip 1st byte (code id) at offset 0
238
+
239
+ if (theBytes.length === 0) {
240
+ ret = "";
241
+ } else {
242
+ ret = prefix + Utils.B58(theBytes);
243
+ }
244
+ } else {
245
+ theBytes = bytes.slice(offset, bsize + offset);
246
+ ret = "0x" + theBytes.toString("hex");
247
+ }
248
+
249
+ bytes = bytes.slice(bsize + offset);
250
+ return ret;
251
+ }
252
+
253
+ var tokenId;
254
+ var qid;
255
+ var nid;
256
+ var scheme;
257
+ var flags;
258
+
259
+ switch (tokenType) {
260
+ case "tqw_": // content write token v1
261
+
262
+ case "tqpw":
263
+ // content part write token v1
264
+ tokenId = "0x" + bytes.toString("hex");
265
+ break;
266
+
267
+ case "tlro": // LRO,
268
+
269
+ case "tq__":
270
+ // content write token
271
+ qid = decodeBytes(true, "iq__");
272
+ nid = decodeBytes(true, "inod");
273
+ tokenId = decodeBytes(false, "");
274
+ break;
275
+
276
+ case "tqp_":
277
+ // content part write token
278
+ if (bytes.length < 3) {
279
+ throw new Error("Invalid write token: [\"".concat(writeToken, "\"] (token truncated)"));
280
+ }
281
+
282
+ scheme = bytes[0];
283
+ flags = bytes[1];
284
+ bytes = bytes.slice(2);
285
+ tokenId = decodeBytes(false, "");
286
+ break;
287
+
288
+ default:
289
+ // already raised
290
+ throw new Error("Invalid write token: [\"".concat(writeToken, "\"] (unknown prefix)"));
291
+ }
292
+
293
+ return {
294
+ tokenType: tokenType,
295
+ // type of token
296
+ tokenId: tokenId,
297
+ // random bytes generated by the fabric node
298
+ objectId: qid,
299
+ // content id for content write token (tq__) or LRO (tlro)
300
+ nodeId: nid,
301
+ // node id where the content write token is valid (tq__)
302
+ scheme: scheme,
303
+ // encryption scheme for part write token - (tqp_)
304
+ flags: flags // flags for part write token (tqp_)
305
+
306
+ };
307
+ },
308
+
164
309
  /**
165
310
  * Convert contract address to multiformat hash
166
311
  *
@@ -259,6 +404,35 @@ var Utils = {
259
404
  return Utils.HashToAddress(firstHash) === Utils.HashToAddress(secondHash);
260
405
  },
261
406
 
407
+ /**
408
+ * Determine whether the address is valid
409
+ *
410
+ * @param {string} address - Address to validate
411
+ *
412
+ * @returns {boolean} - Whether or not the address is valid
413
+ */
414
+ ValidAddress: function ValidAddress(address) {
415
+ try {
416
+ getAddress(address);
417
+ return true;
418
+ } catch (error) {
419
+ _this.Log(error);
420
+
421
+ return false;
422
+ }
423
+ },
424
+
425
+ /**
426
+ * Determine whether the hash is valid
427
+ *
428
+ * @param {string} hash - Hash to validate
429
+ *
430
+ * @returns {boolean} - Whether or not the hash is valid
431
+ */
432
+ ValidHash: function ValidHash(hash) {
433
+ return Utils.ValidAddress(Utils.HashToAddress(hash));
434
+ },
435
+
262
436
  /**
263
437
  * Convert the specified string to a bytes32 string
264
438
  *
@@ -303,203 +477,233 @@ var Utils = {
303
477
  signature = _token$split2[1];
304
478
 
305
479
  info = JSON.parse(Utils.FromB64(info));
306
- return _objectSpread({}, info, {
480
+ return _objectSpread(_objectSpread({}, info), {}, {
307
481
  signature: signature
308
482
  });
309
483
  },
310
- LimitedMap: function LimitedMap(limit, array, f) {
311
- var index, locked, nextIndex, results, active;
312
- return _regeneratorRuntime.async(function LimitedMap$(_context3) {
313
- while (1) {
314
- switch (_context3.prev = _context3.next) {
315
- case 0:
316
- index = 0;
317
- locked = false;
318
-
319
- nextIndex = function nextIndex() {
320
- var thisIndex;
321
- return _regeneratorRuntime.async(function nextIndex$(_context) {
322
- while (1) {
323
- switch (_context.prev = _context.next) {
324
- case 0:
325
- if (!locked) {
326
- _context.next = 5;
327
- break;
484
+ LimitedMap: function () {
485
+ var _LimitedMap = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee3(limit, array, f) {
486
+ var index, locked, nextIndex, results, active;
487
+ return _regeneratorRuntime.wrap(function _callee3$(_context3) {
488
+ while (1) {
489
+ switch (_context3.prev = _context3.next) {
490
+ case 0:
491
+ index = 0;
492
+ locked = false;
493
+
494
+ nextIndex = /*#__PURE__*/function () {
495
+ var _ref = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee() {
496
+ var thisIndex;
497
+ return _regeneratorRuntime.wrap(function _callee$(_context) {
498
+ while (1) {
499
+ switch (_context.prev = _context.next) {
500
+ case 0:
501
+ if (!locked) {
502
+ _context.next = 5;
503
+ break;
504
+ }
505
+
506
+ _context.next = 3;
507
+ return new Promise(function (resolve) {
508
+ return setTimeout(resolve, 10);
509
+ });
510
+
511
+ case 3:
512
+ _context.next = 0;
513
+ break;
514
+
515
+ case 5:
516
+ locked = true;
517
+ thisIndex = index;
518
+ index += 1;
519
+ locked = false;
520
+ return _context.abrupt("return", thisIndex);
521
+
522
+ case 10:
523
+ case "end":
524
+ return _context.stop();
328
525
  }
526
+ }
527
+ }, _callee);
528
+ }));
529
+
530
+ return function nextIndex() {
531
+ return _ref.apply(this, arguments);
532
+ };
533
+ }();
534
+
535
+ results = [];
536
+ active = 0;
537
+ return _context3.abrupt("return", new Promise(function (resolve, reject) {
538
+ _toConsumableArray(Array(limit || 1)).forEach( /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2() {
539
+ var index;
540
+ return _regeneratorRuntime.wrap(function _callee2$(_context2) {
541
+ while (1) {
542
+ switch (_context2.prev = _context2.next) {
543
+ case 0:
544
+ active += 1;
545
+ _context2.next = 3;
546
+ return nextIndex();
547
+
548
+ case 3:
549
+ index = _context2.sent;
550
+
551
+ case 4:
552
+ if (!(index < array.length)) {
553
+ _context2.next = 19;
554
+ break;
555
+ }
556
+
557
+ _context2.prev = 5;
558
+ _context2.next = 8;
559
+ return f(array[index], index);
560
+
561
+ case 8:
562
+ results[index] = _context2.sent;
563
+ _context2.next = 14;
564
+ break;
565
+
566
+ case 11:
567
+ _context2.prev = 11;
568
+ _context2.t0 = _context2["catch"](5);
569
+ reject(_context2.t0);
329
570
 
330
- _context.next = 3;
331
- return _regeneratorRuntime.awrap(new Promise(function (resolve) {
332
- return setTimeout(resolve, 10);
333
- }));
334
-
335
- case 3:
336
- _context.next = 0;
337
- break;
338
-
339
- case 5:
340
- locked = true;
341
- thisIndex = index;
342
- index += 1;
343
- locked = false;
344
- return _context.abrupt("return", thisIndex);
345
-
346
- case 10:
347
- case "end":
348
- return _context.stop();
349
- }
350
- }
351
- });
352
- };
353
-
354
- results = [];
355
- active = 0;
356
- return _context3.abrupt("return", new Promise(function (resolve, reject) {
357
- _toConsumableArray(Array(limit || 1)).forEach(function _callee() {
358
- var index;
359
- return _regeneratorRuntime.async(function _callee$(_context2) {
360
- while (1) {
361
- switch (_context2.prev = _context2.next) {
362
- case 0:
363
- active += 1;
364
- _context2.next = 3;
365
- return _regeneratorRuntime.awrap(nextIndex());
366
-
367
- case 3:
368
- index = _context2.sent;
369
-
370
- case 4:
371
- if (!(index < array.length)) {
372
- _context2.next = 19;
571
+ case 14:
572
+ _context2.next = 16;
573
+ return nextIndex();
574
+
575
+ case 16:
576
+ index = _context2.sent;
577
+ _context2.next = 4;
373
578
  break;
374
- }
375
-
376
- _context2.prev = 5;
377
- _context2.next = 8;
378
- return _regeneratorRuntime.awrap(f(array[index], index));
379
-
380
- case 8:
381
- results[index] = _context2.sent;
382
- _context2.next = 14;
383
- break;
384
-
385
- case 11:
386
- _context2.prev = 11;
387
- _context2.t0 = _context2["catch"](5);
388
- reject(_context2.t0);
389
-
390
- case 14:
391
- _context2.next = 16;
392
- return _regeneratorRuntime.awrap(nextIndex());
393
-
394
- case 16:
395
- index = _context2.sent;
396
- _context2.next = 4;
397
- break;
398
-
399
- case 19:
400
- // When finished and no more workers are active, resolve
401
- active -= 1;
402
-
403
- if (active === 0) {
404
- resolve(results);
405
- }
406
-
407
- case 21:
408
- case "end":
409
- return _context2.stop();
579
+
580
+ case 19:
581
+ // When finished and no more workers are active, resolve
582
+ active -= 1;
583
+
584
+ if (active === 0) {
585
+ resolve(results);
586
+ }
587
+
588
+ case 21:
589
+ case "end":
590
+ return _context2.stop();
591
+ }
410
592
  }
411
- }
412
- }, null, null, [[5, 11]]);
413
- });
414
- }));
415
-
416
- case 6:
417
- case "end":
418
- return _context3.stop();
593
+ }, _callee2, null, [[5, 11]]);
594
+ })));
595
+ }));
596
+
597
+ case 6:
598
+ case "end":
599
+ return _context3.stop();
600
+ }
419
601
  }
420
- }
421
- });
422
- },
423
- ResponseToJson: function ResponseToJson(response) {
424
- return _regeneratorRuntime.async(function ResponseToJson$(_context4) {
425
- while (1) {
426
- switch (_context4.prev = _context4.next) {
427
- case 0:
428
- return _context4.abrupt("return", Utils.ResponseToFormat("json", response));
429
-
430
- case 1:
431
- case "end":
432
- return _context4.stop();
602
+ }, _callee3);
603
+ }));
604
+
605
+ function LimitedMap(_x, _x2, _x3) {
606
+ return _LimitedMap.apply(this, arguments);
607
+ }
608
+
609
+ return LimitedMap;
610
+ }(),
611
+ ResponseToJson: function () {
612
+ var _ResponseToJson = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee4(response) {
613
+ return _regeneratorRuntime.wrap(function _callee4$(_context4) {
614
+ while (1) {
615
+ switch (_context4.prev = _context4.next) {
616
+ case 0:
617
+ return _context4.abrupt("return", Utils.ResponseToFormat("json", response));
618
+
619
+ case 1:
620
+ case "end":
621
+ return _context4.stop();
622
+ }
433
623
  }
434
- }
435
- });
436
- },
437
- ResponseToFormat: function ResponseToFormat(format, response) {
438
- return _regeneratorRuntime.async(function ResponseToFormat$(_context5) {
439
- while (1) {
440
- switch (_context5.prev = _context5.next) {
441
- case 0:
442
- _context5.next = 2;
443
- return _regeneratorRuntime.awrap(response);
624
+ }, _callee4);
625
+ }));
626
+
627
+ function ResponseToJson(_x4) {
628
+ return _ResponseToJson.apply(this, arguments);
629
+ }
444
630
 
445
- case 2:
446
- response = _context5.sent;
447
- _context5.t0 = format.toLowerCase();
448
- _context5.next = _context5.t0 === "json" ? 6 : _context5.t0 === "text" ? 9 : _context5.t0 === "blob" ? 12 : _context5.t0 === "arraybuffer" ? 15 : _context5.t0 === "formdata" ? 18 : _context5.t0 === "buffer" ? 21 : 24;
449
- break;
631
+ return ResponseToJson;
632
+ }(),
633
+ ResponseToFormat: function () {
634
+ var _ResponseToFormat = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee5(format, response) {
635
+ return _regeneratorRuntime.wrap(function _callee5$(_context5) {
636
+ while (1) {
637
+ switch (_context5.prev = _context5.next) {
638
+ case 0:
639
+ _context5.next = 2;
640
+ return response;
450
641
 
451
- case 6:
452
- _context5.next = 8;
453
- return _regeneratorRuntime.awrap(response.json());
642
+ case 2:
643
+ response = _context5.sent;
644
+ _context5.t0 = format.toLowerCase();
645
+ _context5.next = _context5.t0 === "json" ? 6 : _context5.t0 === "text" ? 9 : _context5.t0 === "blob" ? 12 : _context5.t0 === "arraybuffer" ? 15 : _context5.t0 === "formdata" ? 18 : _context5.t0 === "buffer" ? 21 : 24;
646
+ break;
454
647
 
455
- case 8:
456
- return _context5.abrupt("return", _context5.sent);
648
+ case 6:
649
+ _context5.next = 8;
650
+ return response.json();
457
651
 
458
- case 9:
459
- _context5.next = 11;
460
- return _regeneratorRuntime.awrap(response.text());
652
+ case 8:
653
+ return _context5.abrupt("return", _context5.sent);
461
654
 
462
- case 11:
463
- return _context5.abrupt("return", _context5.sent);
655
+ case 9:
656
+ _context5.next = 11;
657
+ return response.text();
464
658
 
465
- case 12:
466
- _context5.next = 14;
467
- return _regeneratorRuntime.awrap(response.blob());
659
+ case 11:
660
+ return _context5.abrupt("return", _context5.sent);
468
661
 
469
- case 14:
470
- return _context5.abrupt("return", _context5.sent);
662
+ case 12:
663
+ _context5.next = 14;
664
+ return response.blob();
471
665
 
472
- case 15:
473
- _context5.next = 17;
474
- return _regeneratorRuntime.awrap(response.arrayBuffer());
666
+ case 14:
667
+ return _context5.abrupt("return", _context5.sent);
475
668
 
476
- case 17:
477
- return _context5.abrupt("return", _context5.sent);
669
+ case 15:
670
+ _context5.next = 17;
671
+ return response.arrayBuffer();
478
672
 
479
- case 18:
480
- _context5.next = 20;
481
- return _regeneratorRuntime.awrap(response.formData());
673
+ case 17:
674
+ return _context5.abrupt("return", _context5.sent);
482
675
 
483
- case 20:
484
- return _context5.abrupt("return", _context5.sent);
676
+ case 18:
677
+ _context5.next = 20;
678
+ return response.formData();
485
679
 
486
- case 21:
487
- _context5.next = 23;
488
- return _regeneratorRuntime.awrap(response.buffer());
680
+ case 20:
681
+ return _context5.abrupt("return", _context5.sent);
489
682
 
490
- case 23:
491
- return _context5.abrupt("return", _context5.sent);
683
+ case 21:
684
+ _context5.next = 23;
685
+ return response.buffer();
492
686
 
493
- case 24:
494
- return _context5.abrupt("return", response);
687
+ case 23:
688
+ return _context5.abrupt("return", _context5.sent);
495
689
 
496
- case 25:
497
- case "end":
498
- return _context5.stop();
690
+ case 24:
691
+ return _context5.abrupt("return", response);
692
+
693
+ case 25:
694
+ case "end":
695
+ return _context5.stop();
696
+ }
499
697
  }
500
- }
501
- });
502
- },
698
+ }, _callee5);
699
+ }));
700
+
701
+ function ResponseToFormat(_x5, _x6) {
702
+ return _ResponseToFormat.apply(this, arguments);
703
+ }
704
+
705
+ return ResponseToFormat;
706
+ }(),
503
707
 
504
708
  /**
505
709
  * Resize the image file or link URL to the specified maximum height. Can also be used to remove
@@ -510,9 +714,9 @@ var Utils = {
510
714
  *
511
715
  * @returns {string} - The modified URL with the height parameter
512
716
  */
513
- ResizeImage: function ResizeImage(_ref) {
514
- var imageUrl = _ref.imageUrl,
515
- height = _ref.height;
717
+ ResizeImage: function ResizeImage(_ref3) {
718
+ var imageUrl = _ref3.imageUrl,
719
+ height = _ref3.height;
516
720
 
517
721
  if (!imageUrl || imageUrl && !imageUrl.startsWith("http")) {
518
722
  return imageUrl;