@ursalock/server 0.4.0 → 0.4.2

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 (2) hide show
  1. package/dist/index.js +37 -34
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -503,19 +503,28 @@ function listDocuments(vaultUid, userId, opts) {
503
503
  function updateDocument(uid, vaultUid, userId, input) {
504
504
  const db = getDb();
505
505
  if (input.version != null) {
506
- const stmt2 = db.prepare(`
507
- UPDATE documents SET data = ?, hmac = ?, version = ? + 1, updated_at = unixepoch()
508
- WHERE uid = ? AND vault_uid = ? AND user_id = ? AND version = ?
509
- RETURNING ${DOCUMENT_COLUMNS}
510
- `);
511
- return stmt2.get(input.data, input.hmac ?? null, input.version, uid, vaultUid, userId, input.version);
506
+ const result = db.transaction(() => {
507
+ const stmt2 = db.prepare(`
508
+ UPDATE documents SET data = ?, hmac = ?, version = ? + 1, updated_at = unixepoch()
509
+ WHERE uid = ? AND vault_uid = ? AND user_id = ? AND version = ?
510
+ RETURNING ${DOCUMENT_COLUMNS}
511
+ `);
512
+ const doc2 = stmt2.get(input.data, input.hmac ?? null, input.version, uid, vaultUid, userId, input.version);
513
+ if (doc2) return { document: doc2, conflict: false };
514
+ const exists = db.prepare(
515
+ `SELECT 1 FROM documents WHERE uid = ? AND vault_uid = ? AND user_id = ?`
516
+ ).get(uid, vaultUid, userId);
517
+ return { document: void 0, conflict: !!exists };
518
+ })();
519
+ return result;
512
520
  }
513
521
  const stmt = db.prepare(`
514
522
  UPDATE documents SET data = ?, hmac = ?, version = version + 1, updated_at = unixepoch()
515
523
  WHERE uid = ? AND vault_uid = ? AND user_id = ?
516
524
  RETURNING ${DOCUMENT_COLUMNS}
517
525
  `);
518
- return stmt.get(input.data, input.hmac ?? null, uid, vaultUid, userId);
526
+ const doc = stmt.get(input.data, input.hmac ?? null, uid, vaultUid, userId);
527
+ return { document: doc, conflict: false };
519
528
  }
520
529
  function softDeleteDocument(uid, vaultUid, userId) {
521
530
  const db = getDb();
@@ -719,10 +728,6 @@ var errors = {
719
728
  insufficient_permissions: { code: "insufficient_permissions", message: "Insufficient permissions" },
720
729
  // Vault errors
721
730
  vault_not_found: { code: "vault_not_found", message: "Vault not found" },
722
- vault_already_exists: (name) => ({
723
- code: "vault_already_exists",
724
- message: `Vault "${name}" already exists`
725
- }),
726
731
  vault_conflict: { code: "vault_conflict", message: "Version conflict - vault has been modified. Please refresh and retry." },
727
732
  invalid_vault_data: { code: "invalid_vault_data", message: "Invalid vault data" },
728
733
  // Document errors
@@ -730,20 +735,25 @@ var errors = {
730
735
  document_conflict: { code: "document_conflict", message: "Version conflict - document has been modified. Please refresh and retry." },
731
736
  document_already_exists: { code: "document_already_exists", message: "Document already exists" },
732
737
  // Validation errors
733
- validation_error: (details) => ({
734
- code: "validation_error",
735
- message: details
736
- }),
737
738
  invalid_request: { code: "invalid_request", message: "Invalid request" },
738
739
  // Server errors
739
740
  internal_error: { code: "internal_error", message: "Internal server error" }
740
741
  };
741
- function getError(code, arg) {
742
- const factory = errors[code];
743
- if (typeof factory === "function") {
744
- return factory(arg ?? "");
742
+ var errorBuilders = {
743
+ vaultAlreadyExists: (name) => ({
744
+ code: "vault_already_exists",
745
+ message: `Vault "${name}" already exists`
746
+ }),
747
+ validationError: (details) => ({
748
+ code: "validation_error",
749
+ message: details
750
+ })
751
+ };
752
+ function getError(code) {
753
+ if (code in errors) {
754
+ return errors[code];
745
755
  }
746
- return factory;
756
+ return { code, message: code.replace(/_/g, " ") };
747
757
  }
748
758
 
749
759
  // src/features/auth/middleware.ts
@@ -1657,7 +1667,7 @@ var VaultService = class {
1657
1667
  createVault(userId, data) {
1658
1668
  const existing = this.vaultRepo.findByName(data.name, userId);
1659
1669
  if (existing) {
1660
- throw new ApiException(errors.vault_already_exists(data.name), 409);
1670
+ throw new ApiException(errorBuilders.vaultAlreadyExists(data.name), 409);
1661
1671
  }
1662
1672
  const vault = this.vaultRepo.create({
1663
1673
  userId,
@@ -1813,8 +1823,9 @@ var DocumentService = class {
1813
1823
  this.vaultRepo = vaultRepo3;
1814
1824
  }
1815
1825
  /**
1816
- * Verify vault ownership before any operation
1817
- * Throws 404 if vault doesn't exist or doesn't belong to user
1826
+ * Verify vault exists and belongs to user.
1827
+ * Only needed for create (to prevent orphan documents referencing non-existent vaults).
1828
+ * Read/update/delete queries already filter by userId — no vault exists = empty result.
1818
1829
  */
1819
1830
  verifyVaultOwnership(vaultUid, userId) {
1820
1831
  const vault = this.vaultRepo.findByUid(vaultUid, userId);
@@ -1840,7 +1851,6 @@ var DocumentService = class {
1840
1851
  * Get document by UID
1841
1852
  */
1842
1853
  getDocument(uid, vaultUid, userId) {
1843
- this.verifyVaultOwnership(vaultUid, userId);
1844
1854
  const document = this.documentRepo.findByUid(uid, vaultUid, userId);
1845
1855
  if (!document) {
1846
1856
  throw new ApiException(errors.document_not_found, 404);
@@ -1851,7 +1861,6 @@ var DocumentService = class {
1851
1861
  * List documents in a vault
1852
1862
  */
1853
1863
  listDocuments(vaultUid, userId, opts) {
1854
- this.verifyVaultOwnership(vaultUid, userId);
1855
1864
  const documents = this.documentRepo.list(vaultUid, userId, opts);
1856
1865
  return {
1857
1866
  documents: documents.map(toDocumentResponse)
@@ -1861,14 +1870,10 @@ var DocumentService = class {
1861
1870
  * Update a document
1862
1871
  */
1863
1872
  updateDocument(uid, vaultUid, userId, data) {
1864
- this.verifyVaultOwnership(vaultUid, userId);
1865
- const document = this.documentRepo.update(uid, vaultUid, userId, data);
1873
+ const { document, conflict } = this.documentRepo.update(uid, vaultUid, userId, data);
1866
1874
  if (!document) {
1867
- if (data.version != null) {
1868
- const existing = this.documentRepo.findByUid(uid, vaultUid, userId);
1869
- if (existing) {
1870
- throw new ApiException(errors.document_conflict, 409);
1871
- }
1875
+ if (conflict) {
1876
+ throw new ApiException(errors.document_conflict, 409);
1872
1877
  }
1873
1878
  throw new ApiException(errors.document_not_found, 404);
1874
1879
  }
@@ -1878,7 +1883,6 @@ var DocumentService = class {
1878
1883
  * Soft delete a document
1879
1884
  */
1880
1885
  deleteDocument(uid, vaultUid, userId) {
1881
- this.verifyVaultOwnership(vaultUid, userId);
1882
1886
  const document = this.documentRepo.softDelete(uid, vaultUid, userId);
1883
1887
  if (!document) {
1884
1888
  throw new ApiException(errors.document_not_found, 404);
@@ -1889,7 +1893,6 @@ var DocumentService = class {
1889
1893
  * Delta sync - get documents modified since timestamp
1890
1894
  */
1891
1895
  syncDocuments(vaultUid, userId, since) {
1892
- this.verifyVaultOwnership(vaultUid, userId);
1893
1896
  const documents = this.documentRepo.getSince(vaultUid, userId, since);
1894
1897
  return {
1895
1898
  documents: documents.map(toDocumentResponse),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ursalock/server",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "description": "Self-hostable ursalock server with SQLite and ZKCredentials support",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",