websql-db.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. "use strict";
  2. var codec = require('../lib/object-codec.js');
  3. var bodec = require('bodec');
  4. var inflate = require('../lib/inflate');
  5. var deflate = require('../lib/deflate');
  6. var sha1 = require('git-sha1');
  7. var modes = require('../lib/modes.js');
  8. var db;
  9. mixin.init = init;
  10. mixin.loadAs = loadAs;
  11. mixin.saveAs = saveAs;
  12. mixin.loadRaw = loadRaw;
  13. mixin.saveRaw = saveRaw;
  14. module.exports = mixin;
  15. function mixin(repo, prefix) {
  16. if (!prefix) throw new Error("Prefix required");
  17. repo.refPrefix = prefix;
  18. repo.saveAs = saveAs;
  19. repo.saveRaw = saveRaw;
  20. repo.loadAs = loadAs;
  21. repo.loadRaw = loadRaw;
  22. repo.readRef = readRef;
  23. repo.updateRef = updateRef;
  24. repo.hasHash = hasHash;
  25. }
  26. function init(callback) {
  27. db = openDatabase('tedit', '1.0', 'tedit local data', 10 * 1024 * 1024);
  28. db.transaction(function (tx) {
  29. tx.executeSql(
  30. 'CREATE TABLE IF NOT EXISTS objects (hash unique, body blob)'
  31. );
  32. tx.executeSql(
  33. 'CREATE TABLE IF NOT EXISTS refs (path unique, value text)'
  34. );
  35. }, function () {
  36. console.error(arguments);
  37. callback(new Error("Problem initializing database"));
  38. }, function () {
  39. callback();
  40. });
  41. }
  42. function saveAs(type, body, callback) {
  43. /*jshint: validthis: true */
  44. if (!callback) return saveAs.bind(this, type, body);
  45. var hash, buffer;
  46. try {
  47. buffer = codec.frame({type:type,body:body});
  48. hash = sha1(buffer);
  49. }
  50. catch (err) { return callback(err); }
  51. this.saveRaw(hash, buffer, callback);
  52. }
  53. function saveRaw(hash, buffer, callback) {
  54. /*jshint: validthis: true */
  55. if (!callback) return saveRaw.bind(this, hash, buffer);
  56. var sql = 'INSERT INTO objects (hash, body) VALUES (?, ?)';
  57. db.transaction(function (tx) {
  58. var text;
  59. try {
  60. text = bodec.toBase64(deflate(buffer));
  61. }
  62. catch (err) {
  63. return callback(err);
  64. }
  65. tx.executeSql(sql, [hash, text], function () {
  66. callback(null, hash);
  67. });
  68. });
  69. }
  70. function loadAs(type, hash, callback) {
  71. /*jshint: validthis: true */
  72. if (!callback) return loadAs.bind(this, type, hash);
  73. loadRaw(hash, function (err, buffer) {
  74. if (!buffer) return callback(err);
  75. var parts, body;
  76. try {
  77. parts = codec.deframe(buffer);
  78. if (parts.type !== type) throw new Error("Type mismatch");
  79. body = codec.decoders[type](parts.body);
  80. }
  81. catch (err) {
  82. return callback(err);
  83. }
  84. callback(null, body);
  85. });
  86. }
  87. function loadRaw(hash, callback) {
  88. /*jshint: validthis: true */
  89. if (!callback) return loadRaw.bind(this, hash);
  90. var sql = 'SELECT * FROM objects WHERE hash=?';
  91. db.readTransaction(function (tx) {
  92. tx.executeSql(sql, [hash], function (tx, result) {
  93. if (!result.rows.length) return callback();
  94. var item = result.rows.item(0);
  95. var buffer;
  96. try {
  97. buffer = inflate(bodec.fromBase64(item.body));
  98. }
  99. catch (err) {
  100. return callback(err);
  101. }
  102. callback(null, buffer);
  103. }, function (tx, error) {
  104. callback(new Error(error.message));
  105. });
  106. });
  107. }
  108. function hasHash(type, hash, callback) {
  109. /*jshint: validthis: true */
  110. loadAs(type, hash, function (err, value) {
  111. if (err) return callback(err);
  112. if (value === undefined) return callback(null, false);
  113. if (type !== "tree") return callback(null, true);
  114. var names = Object.keys(value);
  115. next();
  116. function next() {
  117. if (!names.length) return callback(null, true);
  118. var name = names.pop();
  119. var entry = value[name];
  120. hasHash(modes.toType(entry.mode), entry.hash, function (err, has) {
  121. if (err) return callback(err);
  122. if (has) return next();
  123. callback(null, false);
  124. });
  125. }
  126. });
  127. }
  128. function readRef(ref, callback) {
  129. /*jshint: validthis: true */
  130. var key = this.refPrefix + "/" + ref;
  131. var sql = 'SELECT * FROM refs WHERE path=?';
  132. db.transaction(function (tx) {
  133. tx.executeSql(sql, [key], function (tx, result) {
  134. if (!result.rows.length) return callback();
  135. var item = result.rows.item(0);
  136. callback(null, item.value);
  137. }, function (tx, error) {
  138. callback(new Error(error.message));
  139. });
  140. });
  141. }
  142. function updateRef(ref, hash, callback) {
  143. /*jshint: validthis: true */
  144. var key = this.refPrefix + "/" + ref;
  145. var sql = 'INSERT INTO refs (path, value) VALUES (?, ?)';
  146. db.transaction(function (tx) {
  147. tx.executeSql(sql, [key, hash], function () {
  148. callback();
  149. }, function (tx, error) {
  150. callback(new Error(error.message));
  151. });
  152. });
  153. }