@harperfast/harper 5.2.0-beta.4 → 5.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.
Files changed (169) hide show
  1. package/bin/backup.ts +169 -0
  2. package/bin/cliOperations.ts +92 -73
  3. package/bin/harper.ts +25 -6
  4. package/components/Scope.ts +52 -6
  5. package/components/componentLoader.ts +107 -9
  6. package/components/mcp/toolRegistry.ts +10 -0
  7. package/components/mcp/tools/application.ts +12 -5
  8. package/components/mcp/tools/operations.ts +3 -0
  9. package/components/mcp/tools/schemas/operationDescriptions.ts +3 -0
  10. package/components/mcp/tools/schemas/operations.ts +9 -0
  11. package/components/operations.js +1 -0
  12. package/components/operationsValidation.js +32 -2
  13. package/components/scopeMount.ts +150 -0
  14. package/config-root.schema.json +4 -0
  15. package/dataLayer/backupManifest.ts +102 -0
  16. package/dataLayer/blobBackup.ts +286 -0
  17. package/dataLayer/harperBridge/ResourceBridge.ts +43 -0
  18. package/dataLayer/restoreMarker.ts +276 -0
  19. package/dataLayer/rocksdbBackup.ts +1100 -0
  20. package/dist/bin/backup.d.ts +9 -0
  21. package/dist/bin/backup.js +192 -0
  22. package/dist/bin/backup.js.map +1 -0
  23. package/dist/bin/cliOperations.d.ts +13 -0
  24. package/dist/bin/cliOperations.js +89 -70
  25. package/dist/bin/cliOperations.js.map +1 -1
  26. package/dist/bin/harper.d.ts +7 -0
  27. package/dist/bin/harper.js +27 -6
  28. package/dist/bin/harper.js.map +1 -1
  29. package/dist/components/Scope.d.ts +37 -1
  30. package/dist/components/Scope.js +48 -6
  31. package/dist/components/Scope.js.map +1 -1
  32. package/dist/components/componentLoader.d.ts +2 -6
  33. package/dist/components/componentLoader.js +91 -8
  34. package/dist/components/componentLoader.js.map +1 -1
  35. package/dist/components/mcp/toolRegistry.d.ts +7 -0
  36. package/dist/components/mcp/toolRegistry.js +10 -0
  37. package/dist/components/mcp/toolRegistry.js.map +1 -1
  38. package/dist/components/mcp/tools/application.js +11 -5
  39. package/dist/components/mcp/tools/application.js.map +1 -1
  40. package/dist/components/mcp/tools/operations.js +3 -0
  41. package/dist/components/mcp/tools/operations.js.map +1 -1
  42. package/dist/components/mcp/tools/schemas/operationDescriptions.js +2 -0
  43. package/dist/components/mcp/tools/schemas/operationDescriptions.js.map +1 -1
  44. package/dist/components/mcp/tools/schemas/operations.js +9 -0
  45. package/dist/components/mcp/tools/schemas/operations.js.map +1 -1
  46. package/dist/components/operations.js +2 -0
  47. package/dist/components/operations.js.map +1 -1
  48. package/dist/components/operationsValidation.js +34 -2
  49. package/dist/components/operationsValidation.js.map +1 -1
  50. package/dist/components/scopeMount.d.ts +86 -0
  51. package/dist/components/scopeMount.js +131 -0
  52. package/dist/components/scopeMount.js.map +1 -0
  53. package/dist/dataLayer/backupManifest.d.ts +26 -0
  54. package/dist/dataLayer/backupManifest.js +97 -0
  55. package/dist/dataLayer/backupManifest.js.map +1 -0
  56. package/dist/dataLayer/blobBackup.d.ts +87 -0
  57. package/dist/dataLayer/blobBackup.js +282 -0
  58. package/dist/dataLayer/blobBackup.js.map +1 -0
  59. package/dist/dataLayer/harperBridge/ResourceBridge.d.ts +3 -0
  60. package/dist/dataLayer/harperBridge/ResourceBridge.js +33 -0
  61. package/dist/dataLayer/harperBridge/ResourceBridge.js.map +1 -1
  62. package/dist/dataLayer/restoreMarker.d.ts +122 -0
  63. package/dist/dataLayer/restoreMarker.js +261 -0
  64. package/dist/dataLayer/restoreMarker.js.map +1 -0
  65. package/dist/dataLayer/rocksdbBackup.d.ts +127 -0
  66. package/dist/dataLayer/rocksdbBackup.js +1039 -0
  67. package/dist/dataLayer/rocksdbBackup.js.map +1 -0
  68. package/dist/resources/DatabaseTransaction.js +0 -6
  69. package/dist/resources/DatabaseTransaction.js.map +1 -1
  70. package/dist/resources/ResourceInterface.d.ts +0 -3
  71. package/dist/resources/ResourceInterface.js.map +1 -1
  72. package/dist/resources/Table.js +22 -22
  73. package/dist/resources/Table.js.map +1 -1
  74. package/dist/resources/blob.d.ts +8 -0
  75. package/dist/resources/blob.js +16 -7
  76. package/dist/resources/blob.js.map +1 -1
  77. package/dist/resources/databases.d.ts +42 -1
  78. package/dist/resources/databases.js +276 -40
  79. package/dist/resources/databases.js.map +1 -1
  80. package/dist/resources/transaction.js +0 -3
  81. package/dist/resources/transaction.js.map +1 -1
  82. package/dist/server/REST.js +25 -9
  83. package/dist/server/REST.js.map +1 -1
  84. package/dist/server/fastifyRoutes.js +15 -1
  85. package/dist/server/fastifyRoutes.js.map +1 -1
  86. package/dist/server/itc/serverHandlers.js +7 -1
  87. package/dist/server/itc/serverHandlers.js.map +1 -1
  88. package/dist/server/jobs/jobProcess.js +20 -1
  89. package/dist/server/jobs/jobProcess.js.map +1 -1
  90. package/dist/server/jobs/jobRunner.js +10 -0
  91. package/dist/server/jobs/jobRunner.js.map +1 -1
  92. package/dist/server/jobs/jobs.js +11 -0
  93. package/dist/server/jobs/jobs.js.map +1 -1
  94. package/dist/server/middlewareChain.d.ts +10 -1
  95. package/dist/server/middlewareChain.js +81 -21
  96. package/dist/server/middlewareChain.js.map +1 -1
  97. package/dist/server/serverHelpers/serverHandlers.js +8 -4
  98. package/dist/server/serverHelpers/serverHandlers.js.map +1 -1
  99. package/dist/server/serverHelpers/serverUtilities.js +11 -0
  100. package/dist/server/serverHelpers/serverUtilities.js.map +1 -1
  101. package/dist/server/static.js +11 -3
  102. package/dist/server/static.js.map +1 -1
  103. package/dist/utility/OperationFunctionCaller.js +25 -6
  104. package/dist/utility/OperationFunctionCaller.js.map +1 -1
  105. package/dist/utility/hdbTerms.d.ts +11 -1
  106. package/dist/utility/hdbTerms.js +11 -1
  107. package/dist/utility/hdbTerms.js.map +1 -1
  108. package/dist/utility/logging/harper_logger.d.ts +40 -0
  109. package/dist/utility/logging/harper_logger.js +746 -14
  110. package/dist/utility/logging/harper_logger.js.map +1 -1
  111. package/dist/utility/operation_authorization.js +13 -3
  112. package/dist/utility/operation_authorization.js.map +1 -1
  113. package/npm-shrinkwrap.json +3 -2
  114. package/package.json +4 -1
  115. package/resources/DESIGN.md +2 -0
  116. package/resources/DatabaseTransaction.ts +0 -3
  117. package/resources/ResourceInterface.ts +0 -3
  118. package/resources/Table.ts +20 -20
  119. package/resources/blob.ts +16 -6
  120. package/resources/databases.ts +272 -42
  121. package/resources/transaction.ts +0 -3
  122. package/server/DESIGN.md +16 -0
  123. package/server/REST.ts +25 -9
  124. package/server/fastifyRoutes.ts +20 -1
  125. package/server/itc/serverHandlers.js +7 -1
  126. package/server/jobs/jobProcess.ts +18 -1
  127. package/server/jobs/jobRunner.ts +10 -0
  128. package/server/jobs/jobs.ts +11 -0
  129. package/server/middlewareChain.ts +79 -20
  130. package/server/serverHelpers/serverHandlers.js +8 -4
  131. package/server/serverHelpers/serverUtilities.ts +19 -0
  132. package/server/static.ts +12 -3
  133. package/static/defaultConfig.yaml +1 -0
  134. package/studio/web/assets/{Chat-DK3GlWEb.js → Chat-DoVWScmq.js} +2 -2
  135. package/studio/web/assets/{Chat-DK3GlWEb.js.map → Chat-DoVWScmq.js.map} +1 -1
  136. package/studio/web/assets/{FloatingChat-fBcC1Ew_.js → FloatingChat-UZ2NsUOZ.js} +4 -4
  137. package/studio/web/assets/{FloatingChat-fBcC1Ew_.js.map → FloatingChat-UZ2NsUOZ.js.map} +1 -1
  138. package/studio/web/assets/{apiToken-DJo1nakA.js → apiToken-BUI_04o7.js} +2 -2
  139. package/studio/web/assets/{apiToken-DJo1nakA.js.map → apiToken-BUI_04o7.js.map} +1 -1
  140. package/studio/web/assets/{applications-BDfH8urd.js → applications-D03NA7wW.js} +2 -2
  141. package/studio/web/assets/{applications-BDfH8urd.js.map → applications-D03NA7wW.js.map} +1 -1
  142. package/studio/web/assets/{index-BHo3c2Gk.js → index-Bh_CNAHr.js} +6 -6
  143. package/studio/web/assets/index-Bh_CNAHr.js.map +1 -0
  144. package/studio/web/assets/{index.lazy-BTo0y6UM.js → index.lazy-Dx3MpyDC.js} +4 -4
  145. package/studio/web/assets/{index.lazy-BTo0y6UM.js.map → index.lazy-Dx3MpyDC.js.map} +1 -1
  146. package/studio/web/assets/{notifications-CMxvWNnz.js → notifications-0edoFTsb.js} +2 -2
  147. package/studio/web/assets/{notifications-CMxvWNnz.js.map → notifications-0edoFTsb.js.map} +1 -1
  148. package/studio/web/assets/{notifications-D3GoB26g.js → notifications-CwKhipK7.js} +2 -2
  149. package/studio/web/assets/{notifications-D3GoB26g.js.map → notifications-CwKhipK7.js.map} +1 -1
  150. package/studio/web/assets/{profile-Doj5FVDE.js → profile-DUfEPQtx.js} +2 -2
  151. package/studio/web/assets/{profile-Doj5FVDE.js.map → profile-DUfEPQtx.js.map} +1 -1
  152. package/studio/web/assets/{setComponentFile-yinsqJy0.js → setComponentFile-DMPo4UjC.js} +2 -2
  153. package/studio/web/assets/{setComponentFile-yinsqJy0.js.map → setComponentFile-DMPo4UjC.js.map} +1 -1
  154. package/studio/web/assets/{setup-DJwR0BHd.js → setup-B56Oz1_u.js} +2 -2
  155. package/studio/web/assets/{setup-DJwR0BHd.js.map → setup-B56Oz1_u.js.map} +1 -1
  156. package/studio/web/assets/{status-Br_AbsJs.js → status-BAod7p3o.js} +2 -2
  157. package/studio/web/assets/{status-Br_AbsJs.js.map → status-BAod7p3o.js.map} +1 -1
  158. package/studio/web/assets/{swagger-ui-react-02XH5sVf.js → swagger-ui-react-lQrBxfwM.js} +2 -2
  159. package/studio/web/assets/{swagger-ui-react-02XH5sVf.js.map → swagger-ui-react-lQrBxfwM.js.map} +1 -1
  160. package/studio/web/assets/{tsMode-D1DMKY7h.js → tsMode-CrHCRjTK.js} +2 -2
  161. package/studio/web/assets/{tsMode-D1DMKY7h.js.map → tsMode-CrHCRjTK.js.map} +1 -1
  162. package/studio/web/assets/{useEntityRestURL-DtRblRgw.js → useEntityRestURL-DoaBMEvU.js} +2 -2
  163. package/studio/web/assets/{useEntityRestURL-DtRblRgw.js.map → useEntityRestURL-DoaBMEvU.js.map} +1 -1
  164. package/studio/web/index.html +1 -1
  165. package/utility/OperationFunctionCaller.ts +24 -3
  166. package/utility/hdbTerms.ts +11 -1
  167. package/utility/logging/harper_logger.ts +729 -15
  168. package/utility/operation_authorization.ts +31 -3
  169. package/studio/web/assets/index-BHo3c2Gk.js.map +0 -1
@@ -0,0 +1,261 @@
1
+ 'use strict';
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RESTORING_MARKER_SUFFIX = exports.RESTORE_LOCK_SUFFIX = exports.RESTORE_META_DIR = void 0;
4
+ exports.restoreMetaDir = restoreMetaDir;
5
+ exports.restoreLockPath = restoreLockPath;
6
+ exports.restoringMarkerPath = restoringMarkerPath;
7
+ exports.restoreMarkerPresent = restoreMarkerPresent;
8
+ exports.checkRestoreState = checkRestoreState;
9
+ exports.acquireRestoreLock = acquireRestoreLock;
10
+ exports.releaseRestoreLock = releaseRestoreLock;
11
+ exports.beginRestore = beginRestore;
12
+ exports.completeRestore = completeRestore;
13
+ exports.abandonRestore = abandonRestore;
14
+ exports.clearRestoreMarker = clearRestoreMarker;
15
+ exports.scanBlockedRestores = scanBlockedRestores;
16
+ const node_fs_1 = require("node:fs");
17
+ const node_path_1 = require("node:path");
18
+ const node_crypto_1 = require("node:crypto");
19
+ const rocksdb_js_1 = require("@harperfast/rocksdb-js");
20
+ /**
21
+ * Restore lock + marker protocol for RocksDB database restores (online operation and offline CLI),
22
+ * and the shared per-database exclusion used by `dropDatabase` so a drop and a restore can never
23
+ * mutate the same directory concurrently.
24
+ *
25
+ * Restore metadata lives in an isolated `` `restore` `` directory *beside* the database directory
26
+ * (never inside it, since a restore purges the destination). Each database's two files are keyed by
27
+ * a hash of the database directory name rather than being suffixed onto the name itself. That keeps
28
+ * them out of the database-name namespace — a legal database literally named `orders.restoring`
29
+ * would otherwise be mistaken for the restore marker of `orders`, and a 250-character name plus a
30
+ * `.restore.lock` suffix would exceed the 255-byte `NAME_MAX` on most filesystems. The directory
31
+ * name deliberately contains a backtick: `schemaRegex` (the database-name validator) forbids only
32
+ * `/` and `` ` `` among filesystem-legal characters, so no legal database can ever occupy this path
33
+ * — including a database literally named `.restore` (which *is* a legal name, so a plain `.restore`
34
+ * directory would collide with it and land the markers inside the live database). The directory is
35
+ * not itself a RocksDB/LMDB database (no `CURRENT`/`MANIFEST-`/`.mdb`), so the startup scan ignores
36
+ * it, and no user can create a database that resolves to it.
37
+ *
38
+ * - `<meta-dir>/<key>.lock` — an OS-level exclusive file lock (via rocksdb-js `tryFileLock`),
39
+ * effective across processes, containers, and worker threads, auto-released on process exit.
40
+ * Only *held-ness* is meaningful; the file itself persists after release (harmless). Held for the
41
+ * duration of a restore, and briefly by `dropDatabase` so the two serialize on the same primitive.
42
+ * Known limitation: the lock is owned by the process, so if the restore job's worker *thread*
43
+ * dies without the process exiting, the lock stays held (restores 409) until Harper restarts.
44
+ * - `<meta-dir>/<key>.restoring` — the completion marker. Written (and fsynced) after the lock is
45
+ * acquired and before the destructive restore begins; deleted only after the restore completes
46
+ * successfully, while still holding the lock. Its *existence* means "a restore started and has
47
+ * not finished successfully". Its first line records the database directory name so the startup
48
+ * scan can map a marker back to the database it blocks without decoding the hashed key.
49
+ */
50
+ // The backtick makes this an illegal database name (schemaRegex rejects `/` and backtick only), so
51
+ // it can never collide with a real database directory — see the module header.
52
+ exports.RESTORE_META_DIR = '`restore`';
53
+ exports.RESTORE_LOCK_SUFFIX = '.lock';
54
+ exports.RESTORING_MARKER_SUFFIX = '.restoring';
55
+ /**
56
+ * Directory holding the restore metadata for a database — the reserved `` `restore` `` sibling of
57
+ * the database directory (see the module header for why the name contains a backtick). Shared by
58
+ * every database under the same parent, so a single readdir surfaces all pending restores during
59
+ * the startup scan.
60
+ */
61
+ function restoreMetaDir(dbPath) {
62
+ return (0, node_path_1.join)((0, node_path_1.dirname)(dbPath), exports.RESTORE_META_DIR);
63
+ }
64
+ /**
65
+ * Filesystem-safe, length-bounded key for a database's restore metadata files. Hashing the
66
+ * database directory name (not the full path, so it is stable regardless of where the databases
67
+ * root lives) keeps the metadata filenames short and collision-free while staying independent of
68
+ * the database-name namespace. Database directory names are unique within a databases root, so
69
+ * their hashes are too.
70
+ */
71
+ function restoreMetaKey(dbPath) {
72
+ return (0, node_crypto_1.createHash)('sha256').update((0, node_path_1.basename)(dbPath)).digest('hex').slice(0, 32);
73
+ }
74
+ function restoreLockPath(dbPath) {
75
+ return (0, node_path_1.join)(restoreMetaDir(dbPath), restoreMetaKey(dbPath) + exports.RESTORE_LOCK_SUFFIX);
76
+ }
77
+ function restoringMarkerPath(dbPath) {
78
+ return (0, node_path_1.join)(restoreMetaDir(dbPath), restoreMetaKey(dbPath) + exports.RESTORING_MARKER_SUFFIX);
79
+ }
80
+ /**
81
+ * Whether a `.restoring` marker exists for a database. Cheaper than `checkRestoreState` and, unlike
82
+ * it, safe to call while *this* thread holds the restore lock: `checkRestoreState` would re-probe
83
+ * the lock (which reads as held from the same thread) and report 'in-progress' rather than telling
84
+ * a caller that a *leftover* marker is present. `dropDatabase` uses this after acquiring the lock to
85
+ * distinguish debris from a crashed restore.
86
+ */
87
+ function restoreMarkerPresent(dbPath) {
88
+ return (0, node_fs_1.existsSync)(restoringMarkerPath(dbPath));
89
+ }
90
+ /**
91
+ * Determine the restore state of a database directory. Used by startup database detection and
92
+ * the open-database guards:
93
+ * - 'in-progress': marker present and the restore lock is held (a restore is running in some
94
+ * process) — do not load.
95
+ * - 'incomplete': marker present but the lock is free (crashed mid-restore; the directory may
96
+ * be partial garbage) — do not load; rerun the restore.
97
+ * - 'clear': no marker — load normally (a stale, unheld lock file alone is fine).
98
+ *
99
+ * The marker is checked FIRST and the lock is only probed when the marker exists. Probing takes
100
+ * and releases the flock, and probes are mutually exclusive across threads — if every rescan on
101
+ * every thread probed the (persistent) lock file of a long-ago-restored database, concurrent
102
+ * rescans would collide and misclassify healthy databases as 'in-progress'. Marker-first is
103
+ * safe: `beginRestore` writes (and fsyncs) the marker immediately after taking the lock and
104
+ * before any destructive step, so a database without a marker has nothing to protect yet.
105
+ */
106
+ function checkRestoreState(dbPath) {
107
+ if (!(0, node_fs_1.existsSync)(restoringMarkerPath(dbPath)))
108
+ return 'clear';
109
+ const lockPath = restoreLockPath(dbPath);
110
+ if ((0, node_fs_1.existsSync)(lockPath)) {
111
+ const token = (0, rocksdb_js_1.tryFileLock)(lockPath);
112
+ if (token === 0)
113
+ return 'in-progress';
114
+ (0, rocksdb_js_1.fileLockRelease)(token);
115
+ }
116
+ return 'incomplete';
117
+ }
118
+ /**
119
+ * fsync a directory so a create/unlink of an entry within it is durable. Best-effort: Windows (and
120
+ * some filesystems) reject opening a directory for fsync with EPERM/EISDIR/ENOTSUP — the durability
121
+ * flush is a POSIX nicety, so treat those as a no-op rather than failing the restore.
122
+ */
123
+ function fsyncDir(dir) {
124
+ let dirFd;
125
+ try {
126
+ dirFd = (0, node_fs_1.openSync)(dir, 'r');
127
+ }
128
+ catch (error) {
129
+ if (error.code === 'EPERM' || error.code === 'EISDIR' || error.code === 'ENOTSUP')
130
+ return;
131
+ throw error;
132
+ }
133
+ try {
134
+ (0, node_fs_1.fsyncSync)(dirFd);
135
+ }
136
+ finally {
137
+ (0, node_fs_1.closeSync)(dirFd);
138
+ }
139
+ }
140
+ /**
141
+ * Take the per-database restore lock without writing a marker. Used by `dropDatabase` so a drop and
142
+ * a restore serialize on the same primitive: whichever takes the lock first runs to completion; the
143
+ * other gets a 409. Throws (statusCode 409) if the lock is already held.
144
+ */
145
+ function acquireRestoreLock(dbPath) {
146
+ (0, node_fs_1.mkdirSync)(restoreMetaDir(dbPath), { recursive: true });
147
+ const token = (0, rocksdb_js_1.tryFileLock)(restoreLockPath(dbPath));
148
+ if (token === 0) {
149
+ const error = new Error(`Restore already in progress for database at ${dbPath}`);
150
+ error.statusCode = 409;
151
+ throw error;
152
+ }
153
+ return { token, dbPath, preexisting: false };
154
+ }
155
+ /**
156
+ * Release a lock taken by `acquireRestoreLock` (no marker to remove).
157
+ */
158
+ function releaseRestoreLock(lock) {
159
+ (0, rocksdb_js_1.fileLockRelease)(lock.token);
160
+ }
161
+ /**
162
+ * Acquire the per-database restore lock and write the restoring marker. Call before any
163
+ * destructive step. Returns the lock (with `preexisting` set when a marker was already present, so
164
+ * a failed recovery attempt knows not to clear it). Throws (statusCode 409) if another restore
165
+ * already holds the lock.
166
+ */
167
+ function beginRestore(dbPath) {
168
+ const markerPath = restoringMarkerPath(dbPath);
169
+ const preexisting = (0, node_fs_1.existsSync)(markerPath);
170
+ const lock = acquireRestoreLock(dbPath);
171
+ try {
172
+ const fd = (0, node_fs_1.openSync)(markerPath, 'w');
173
+ try {
174
+ // first line is the database directory name so the startup scan can map this marker back to
175
+ // the database it blocks without reversing the hashed key
176
+ (0, node_fs_1.writeSync)(fd, `${(0, node_path_1.basename)(dbPath)}\nrestore started ${new Date().toISOString()}\n`);
177
+ (0, node_fs_1.fsyncSync)(fd);
178
+ }
179
+ finally {
180
+ (0, node_fs_1.closeSync)(fd);
181
+ }
182
+ // fsync the metadata directory so the marker's directory entry is durable — without this a
183
+ // power loss can lose the entry, and a half-purged database would load as healthy
184
+ fsyncDir(restoreMetaDir(dbPath));
185
+ }
186
+ catch (error) {
187
+ (0, rocksdb_js_1.fileLockRelease)(lock.token);
188
+ throw error;
189
+ }
190
+ return { ...lock, preexisting };
191
+ }
192
+ /**
193
+ * Mark the restore successful: delete the marker (while still holding the lock), then release
194
+ * the lock.
195
+ */
196
+ function completeRestore(lock) {
197
+ try {
198
+ (0, node_fs_1.unlinkSync)(restoringMarkerPath(lock.dbPath));
199
+ // fsync the metadata directory so the marker's *removal* is durable — symmetric with the
200
+ // creation fsync in beginRestore. Without it, a power loss could resurrect the marker's
201
+ // directory entry and misclassify a fully-restored database as incomplete.
202
+ fsyncDir(restoreMetaDir(lock.dbPath));
203
+ }
204
+ finally {
205
+ (0, rocksdb_js_1.fileLockRelease)(lock.token);
206
+ }
207
+ }
208
+ /**
209
+ * Release the lock after a failed restore, leaving the marker in place so the database is
210
+ * detected as an incomplete restore (and not loaded) until a rerun succeeds.
211
+ */
212
+ function abandonRestore(lock) {
213
+ (0, rocksdb_js_1.fileLockRelease)(lock.token);
214
+ }
215
+ /**
216
+ * Remove a database's restore marker if one is present, then release the lock. Used by
217
+ * `dropDatabase`: a dropped database that carried an incomplete-restore marker should not leave the
218
+ * marker behind to block a future database of the same name. No-op on the marker if none exists.
219
+ */
220
+ function clearRestoreMarker(lock) {
221
+ try {
222
+ const markerPath = restoringMarkerPath(lock.dbPath);
223
+ if ((0, node_fs_1.existsSync)(markerPath)) {
224
+ (0, node_fs_1.unlinkSync)(markerPath);
225
+ fsyncDir(restoreMetaDir(lock.dbPath));
226
+ }
227
+ }
228
+ finally {
229
+ (0, rocksdb_js_1.fileLockRelease)(lock.token);
230
+ }
231
+ }
232
+ /**
233
+ * Scan a databases root's reserved `` `restore` `` metadata directory and report every database currently blocked from
234
+ * loading, mapping each surviving marker back to its database name via the marker's first line.
235
+ * Returns `[dbName, state]` pairs for markers whose state is `in-progress` or `incomplete`
236
+ * (a `clear` result means the marker was removed concurrently and the database is loadable).
237
+ */
238
+ function scanBlockedRestores(databasesRoot) {
239
+ const metaDir = (0, node_path_1.join)(databasesRoot, exports.RESTORE_META_DIR);
240
+ if (!(0, node_fs_1.existsSync)(metaDir))
241
+ return [];
242
+ const blocked = [];
243
+ for (const entry of (0, node_fs_1.readdirSync)(metaDir, { withFileTypes: true })) {
244
+ if (!entry.isFile() || !entry.name.endsWith(exports.RESTORING_MARKER_SUFFIX))
245
+ continue;
246
+ let dbName;
247
+ try {
248
+ dbName = (0, node_fs_1.readFileSync)((0, node_path_1.join)(metaDir, entry.name), 'utf8').split('\n', 1)[0];
249
+ }
250
+ catch {
251
+ continue; // marker removed concurrently
252
+ }
253
+ if (!dbName)
254
+ continue;
255
+ const state = checkRestoreState((0, node_path_1.join)(databasesRoot, dbName));
256
+ if (state !== 'clear')
257
+ blocked.push([dbName, state]);
258
+ }
259
+ return blocked;
260
+ }
261
+ //# sourceMappingURL=restoreMarker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"restoreMarker.js","sourceRoot":"","sources":["../../dataLayer/restoreMarker.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,127 @@
1
+ import { PassThrough } from 'node:stream';
2
+ import { RocksDatabase, type BackupInfo } from '@harperfast/rocksdb-js';
3
+ import { ClientError } from '../utility/errors/hdbError.ts';
4
+ /**
5
+ * Shared core for the RocksDB managed-backup operations (`create_backup`, `list_backups`,
6
+ * `verify_backup`, `delete_backup`, `purge_backups`, `restore_backup`) and the RocksDB path of
7
+ * `get_backup`. Used by both the operation API (running server) and the CLI (stopped server) so
8
+ * the two behave identically.
9
+ *
10
+ * Directory backups are confined to `<backupsRoot>/<database>/` where the backups root comes
11
+ * from the `storage.backupPath` config (default `<hdb_root>/backups`); operations never accept
12
+ * arbitrary filesystem paths.
13
+ */
14
+ export declare class BackupNotFoundError extends ClientError {
15
+ constructor(message: string);
16
+ }
17
+ export declare class BackupInProgressError extends ClientError {
18
+ constructor(message: string);
19
+ }
20
+ export declare function getBackupsRoot(): string;
21
+ /**
22
+ * The database name becomes a path segment under the backups root and the databases root —
23
+ * reject anything that could traverse outside them.
24
+ */
25
+ export declare function validateDatabaseName(databaseName: any): void;
26
+ export declare function backupDirForDatabase(databaseName: string): string;
27
+ /**
28
+ * Resolve the single root store for a database. A database can span multiple root stores when a
29
+ * table has a per-table `path` config; backing up such a database is not supported and errors
30
+ * descriptively. Engine gating (RocksDB vs LMDB) is done inline at each call site.
31
+ */
32
+ export declare function resolveSingleRootStore(databaseName: string): any;
33
+ export declare function listBackupsInDir(backupDir: string): Promise<BackupInfo[]>;
34
+ export declare function listBackups(request: any): Promise<{
35
+ backup_id: number;
36
+ timestamp: number;
37
+ size: number;
38
+ file_count: number;
39
+ blobs: boolean;
40
+ }[]>;
41
+ export declare function deleteBackup(request: any): Promise<{
42
+ ok: boolean;
43
+ }>;
44
+ export declare function purgeBackups(request: any): Promise<{
45
+ deleted: number;
46
+ remaining: number;
47
+ }>;
48
+ export declare function validateCreateBackup(request: any): Promise<void>;
49
+ export declare function createBackup(request: any): Promise<{
50
+ size?: number;
51
+ timestamp?: any;
52
+ database: string;
53
+ backup_id: any;
54
+ blobs: boolean;
55
+ }>;
56
+ export declare function validateVerifyBackup(request: any): Promise<void>;
57
+ export declare function verifyBackup(request: any): Promise<{
58
+ database: string;
59
+ backup_id: number;
60
+ ok: boolean;
61
+ blobs: boolean;
62
+ }>;
63
+ export declare function validateRestoreBackup(request: any): Promise<void>;
64
+ /**
65
+ * Online restore of a user database (see the design's restore lock + marker protocol):
66
+ * take the per-database restore lock, write the restoring marker, close the database across all
67
+ * worker threads, restore, delete the marker, release the lock, and reload everywhere.
68
+ */
69
+ export declare function restoreBackup(request: any): Promise<{
70
+ database: string;
71
+ backup_id: number;
72
+ }>;
73
+ /**
74
+ * Returns a Readable (with `.headers`) streaming a full-snapshot tar (optionally gzipped) of the
75
+ * database's current state. No scratch disk; a consumer error aborts the native backup cleanly.
76
+ * `noCompression` opts out of serverHandlers' accept-encoding auto-gzip — this response must never
77
+ * be compressed by the server.
78
+ *
79
+ * With blobs included (the default; `excludeBlobs` opts out), the database's file-backed blob roots
80
+ * are appended to the same archive under `blobs/<rootIndex>/<relpath>` so a downloaded backup is a
81
+ * complete Harper database. Blob capture is best-effort point-in-time (the archive contains whatever
82
+ * files exist while it streams); a blob deleted mid-stream is skipped.
83
+ */
84
+ export declare function createBackupStream(rootStore: RocksDatabase, databaseName: string, gzip: boolean, excludeBlobs?: boolean): PassThrough;
85
+ /**
86
+ * Offline create: open the RocksDatabase directly, run an ordinary incremental directory backup
87
+ * into the configured backup root, and close. RocksDB is single-writer, so this collides on the
88
+ * database lock if the server is running — callers guard on the server being stopped.
89
+ */
90
+ export declare function createBackupOffline(databaseName: string, excludeBlobs?: boolean): Promise<{
91
+ size?: number;
92
+ timestamp?: any;
93
+ database: string;
94
+ backup_id: any;
95
+ blobs: boolean;
96
+ }>;
97
+ /**
98
+ * Offline restore (required for the `system` database; works for any database). Runs the same
99
+ * lock + marker protocol as the online operation so a crashed CLI restore is detected at next
100
+ * server start. `targetDatabase` restores into a different database directory (non-destructive
101
+ * for the source database); the server picks it up on next start via normal engine detection.
102
+ */
103
+ export declare function restoreBackupOffline(databaseName: string, backupId?: number, targetDatabase?: string): Promise<{
104
+ database: string;
105
+ backup_id: number;
106
+ restored_to: string;
107
+ }>;
108
+ export declare function listBackupsOffline(databaseName: string): Promise<{
109
+ backup_id: number;
110
+ timestamp: number;
111
+ size: number;
112
+ file_count: number;
113
+ blobs: boolean;
114
+ }[]>;
115
+ export declare function verifyBackupOffline(databaseName: string, backupId: number, verifyChecksum?: boolean): Promise<{
116
+ database: string;
117
+ backup_id: number;
118
+ ok: boolean;
119
+ blobs: boolean;
120
+ }>;
121
+ export declare function deleteBackupOffline(databaseName: string, backupId: number): Promise<{
122
+ ok: boolean;
123
+ }>;
124
+ export declare function purgeBackupsOffline(databaseName: string, keepCount: number): Promise<{
125
+ deleted: number;
126
+ remaining: number;
127
+ }>;