@serve.zone/gitops 32.1.0 → 32.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 (74) hide show
  1. package/changelog.md +18 -0
  2. package/deno.json +1 -1
  3. package/dist_ts/00_commitinfo_data.js +1 -1
  4. package/dist_ts/classes/actionlog.d.ts +1 -1
  5. package/dist_ts/classes/actionlog.js +1 -1
  6. package/dist_ts/classes/connectionmanager.d.ts +1 -5
  7. package/dist_ts/classes/connectionmanager.js +5 -28
  8. package/dist_ts/classes/gitopsapp.d.ts +8 -3
  9. package/dist_ts/classes/gitopsapp.js +29 -18
  10. package/dist_ts/{cache/classes.secrets.scan.service.d.ts → classes/secrets.scan.service.d.ts} +1 -1
  11. package/dist_ts/{cache/classes.secrets.scan.service.js → classes/secrets.scan.service.js} +3 -3
  12. package/dist_ts/{cache → db}/classes.cache.cleaner.d.ts +1 -3
  13. package/dist_ts/db/classes.cache.cleaner.js +59 -0
  14. package/dist_ts/{cache → db}/classes.cached.document.d.ts +1 -1
  15. package/dist_ts/{cache → db}/classes.cached.document.js +2 -2
  16. package/dist_ts/db/classes.gitops-db.d.ts +34 -0
  17. package/dist_ts/db/classes.gitops-db.js +77 -0
  18. package/dist_ts/{cache → db}/documents/classes.cached.project.js +3 -3
  19. package/dist_ts/{cache → db}/documents/classes.cached.secret.js +3 -3
  20. package/dist_ts/db/documents/classes.storage.record.d.ts +14 -0
  21. package/dist_ts/db/documents/classes.storage.record.js +82 -0
  22. package/dist_ts/{cache → db}/documents/index.d.ts +1 -0
  23. package/dist_ts/db/documents/index.js +4 -0
  24. package/dist_ts/db/index.d.ts +5 -0
  25. package/dist_ts/db/index.js +5 -0
  26. package/dist_ts/paths.d.ts +2 -2
  27. package/dist_ts/paths.js +2 -3
  28. package/dist_ts/plugins.d.ts +2 -3
  29. package/dist_ts/plugins.js +6 -4
  30. package/dist_ts/storage/classes.storagemanager.d.ts +7 -16
  31. package/dist_ts/storage/classes.storagemanager.js +54 -94
  32. package/dist_ts/storage/index.d.ts +0 -1
  33. package/dist_ts_migrations/classes.migration-record.d.ts +11 -0
  34. package/dist_ts_migrations/classes.migration-record.js +80 -0
  35. package/dist_ts_migrations/import-filesystem-records.d.ts +9 -0
  36. package/dist_ts_migrations/import-filesystem-records.js +74 -0
  37. package/dist_ts_migrations/index.d.ts +12 -0
  38. package/dist_ts_migrations/index.js +24 -0
  39. package/dist_ts_migrations/interfaces.d.ts +16 -0
  40. package/dist_ts_migrations/interfaces.js +2 -0
  41. package/package.json +6 -4
  42. package/readme.md +21 -14
  43. package/ts/00_commitinfo_data.ts +1 -1
  44. package/ts/classes/actionlog.ts +1 -1
  45. package/ts/classes/connectionmanager.ts +4 -27
  46. package/ts/classes/gitopsapp.ts +36 -17
  47. package/ts/{cache/classes.secrets.scan.service.ts → classes/secrets.scan.service.ts} +3 -3
  48. package/ts/{cache → db}/classes.cache.cleaner.ts +1 -4
  49. package/ts/{cache → db}/classes.cached.document.ts +1 -1
  50. package/ts/db/classes.gitops-db.ts +92 -0
  51. package/ts/{cache → db}/documents/classes.cached.project.ts +2 -2
  52. package/ts/{cache → db}/documents/classes.cached.secret.ts +2 -2
  53. package/ts/db/documents/classes.storage.record.ts +26 -0
  54. package/ts/{cache → db}/documents/index.ts +1 -0
  55. package/ts/db/index.ts +5 -0
  56. package/ts/paths.ts +3 -4
  57. package/ts/plugins.ts +5 -3
  58. package/ts/storage/classes.storagemanager.ts +59 -95
  59. package/ts/storage/index.ts +0 -1
  60. package/ts_migrations/classes.migration-record.ts +23 -0
  61. package/ts_migrations/import-filesystem-records.ts +79 -0
  62. package/ts_migrations/index.ts +31 -0
  63. package/ts_migrations/interfaces.ts +18 -0
  64. package/ts_web/00_commitinfo_data.ts +1 -1
  65. package/dist_ts/cache/classes.cache.cleaner.js +0 -61
  66. package/dist_ts/cache/classes.cachedb.d.ts +0 -22
  67. package/dist_ts/cache/classes.cachedb.js +0 -58
  68. package/dist_ts/cache/documents/index.js +0 -3
  69. package/dist_ts/cache/index.d.ts +0 -7
  70. package/dist_ts/cache/index.js +0 -6
  71. package/ts/cache/classes.cachedb.ts +0 -72
  72. package/ts/cache/index.ts +0 -7
  73. /package/dist_ts/{cache → db}/documents/classes.cached.project.d.ts +0 -0
  74. /package/dist_ts/{cache → db}/documents/classes.cached.secret.d.ts +0 -0
@@ -0,0 +1,80 @@
1
+ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
2
+ function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
3
+ var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
4
+ var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
5
+ var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
6
+ var _, done = false;
7
+ for (var i = decorators.length - 1; i >= 0; i--) {
8
+ var context = {};
9
+ for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
10
+ for (var p in contextIn.access) context.access[p] = contextIn.access[p];
11
+ context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
12
+ var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
13
+ if (kind === "accessor") {
14
+ if (result === void 0) continue;
15
+ if (result === null || typeof result !== "object") throw new TypeError("Object expected");
16
+ if (_ = accept(result.get)) descriptor.get = _;
17
+ if (_ = accept(result.set)) descriptor.set = _;
18
+ if (_ = accept(result.init)) initializers.unshift(_);
19
+ }
20
+ else if (_ = accept(result)) {
21
+ if (kind === "field") initializers.unshift(_);
22
+ else descriptor[key] = _;
23
+ }
24
+ }
25
+ if (target) Object.defineProperty(target, contextIn.name, descriptor);
26
+ done = true;
27
+ };
28
+ var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
29
+ var useValue = arguments.length > 2;
30
+ for (var i = 0; i < initializers.length; i++) {
31
+ value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
32
+ }
33
+ return useValue ? value : void 0;
34
+ };
35
+ import * as plugins from '../dist_ts/plugins.js';
36
+ import { GitopsDb } from '../dist_ts/db/index.js';
37
+ /**
38
+ * The ledger of migrations that have run to completion.
39
+ *
40
+ * `migration` owns the stored `_id`, so a migration is looked up by primary key and can
41
+ * only ever be recorded once.
42
+ */
43
+ let MigrationRecord = (() => {
44
+ let _classDecorators = [plugins.smartdata.Collection(() => GitopsDb.getInstance().getDb(), {
45
+ identityAsDocumentId: 'migration',
46
+ })];
47
+ let _classDescriptor;
48
+ let _classExtraInitializers = [];
49
+ let _classThis;
50
+ let _classSuper = plugins.smartdata.SmartDataDbDoc;
51
+ let _migration_decorators;
52
+ let _migration_initializers = [];
53
+ let _migration_extraInitializers = [];
54
+ let _completedAt_decorators;
55
+ let _completedAt_initializers = [];
56
+ let _completedAt_extraInitializers = [];
57
+ var MigrationRecord = class extends _classSuper {
58
+ static { _classThis = this; }
59
+ static {
60
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
61
+ _migration_decorators = [plugins.smartdata.unI(), plugins.smartdata.svDb()];
62
+ _completedAt_decorators = [plugins.smartdata.svDb()];
63
+ __esDecorate(null, null, _migration_decorators, { kind: "field", name: "migration", static: false, private: false, access: { has: obj => "migration" in obj, get: obj => obj.migration, set: (obj, value) => { obj.migration = value; } }, metadata: _metadata }, _migration_initializers, _migration_extraInitializers);
64
+ __esDecorate(null, null, _completedAt_decorators, { kind: "field", name: "completedAt", static: false, private: false, access: { has: obj => "completedAt" in obj, get: obj => obj.completedAt, set: (obj, value) => { obj.completedAt = value; } }, metadata: _metadata }, _completedAt_initializers, _completedAt_extraInitializers);
65
+ __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
66
+ MigrationRecord = _classThis = _classDescriptor.value;
67
+ if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
68
+ __runInitializers(_classThis, _classExtraInitializers);
69
+ }
70
+ migration = __runInitializers(this, _migration_initializers, void 0);
71
+ completedAt = (__runInitializers(this, _migration_extraInitializers), __runInitializers(this, _completedAt_initializers, void 0));
72
+ constructor() {
73
+ super(...arguments);
74
+ __runInitializers(this, _completedAt_extraInitializers);
75
+ }
76
+ };
77
+ return MigrationRecord = _classThis;
78
+ })();
79
+ export { MigrationRecord };
80
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5taWdyYXRpb24tcmVjb3JkLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vdHNfbWlncmF0aW9ucy9jbGFzc2VzLm1pZ3JhdGlvbi1yZWNvcmQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sa0JBQWtCLENBQUM7QUFDNUMsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLG1CQUFtQixDQUFDO0FBRTdDOzs7OztHQUtHO0lBSVUsZUFBZTs0QkFIM0IsT0FBTyxDQUFDLFNBQVMsQ0FBQyxVQUFVLENBQUMsR0FBRyxFQUFFLENBQUMsUUFBUSxDQUFDLFdBQVcsRUFBRSxDQUFDLEtBQUssRUFBRSxFQUFFO1lBQ2xFLG9CQUFvQixFQUFFLFdBQVc7U0FDbEMsQ0FBQzs7OztzQkFDbUMsT0FBTyxDQUFDLFNBQVMsQ0FBQyxjQUFjOzs7Ozs7OytCQUF4QyxTQUFRLFdBR3BDOzs7O3FDQUNFLE9BQU8sQ0FBQyxTQUFTLENBQUMsR0FBRyxFQUFFLEVBQ3ZCLE9BQU8sQ0FBQyxTQUFTLENBQUMsSUFBSSxFQUFFO3VDQUd4QixPQUFPLENBQUMsU0FBUyxDQUFDLElBQUksRUFBRTtZQUZ6Qiw2S0FBTyxTQUFTLDZCQUFULFNBQVMsNkZBQVU7WUFHMUIsbUxBQU8sV0FBVyw2QkFBWCxXQUFXLGlHQUFVO1lBVDlCLDZLQVVDOzs7WUFWWSx1REFBZTs7UUFNbkIsU0FBUyw0REFBVTtRQUduQixXQUFXLHVIQUFVOzs7Ozs7OztTQVRqQixlQUFlIn0=
@@ -0,0 +1,9 @@
1
+ import type { IMigration } from './interfaces.js';
2
+ /**
3
+ * Imports the key/value records that earlier releases kept as files.
4
+ *
5
+ * Both sources are read once and never written back: the ledger, not a renamed file, is
6
+ * what records that this ran. The legacy tree is left on disk untouched so an operator can
7
+ * archive it after confirming the import.
8
+ */
9
+ export declare const importFilesystemRecords: IMigration;
@@ -0,0 +1,74 @@
1
+ import * as plugins from '../dist_ts/plugins.js';
2
+ /**
3
+ * Reads every regular file below `directoryArg` and yields it as a storage key relative to
4
+ * the tree root, so `<root>/connections/a.json` becomes `/connections/a.json`.
5
+ */
6
+ const readDirectoryEntries = async (directoryArg) => {
7
+ try {
8
+ return await plugins.fs.readdir(directoryArg, { withFileTypes: true });
9
+ }
10
+ catch (error) {
11
+ if (error.code === 'ENOENT')
12
+ return [];
13
+ throw error;
14
+ }
15
+ };
16
+ const readLegacyTree = async (directoryArg, prefixArg) => {
17
+ const entries = await readDirectoryEntries(directoryArg);
18
+ const records = [];
19
+ for (const entry of entries) {
20
+ const entryPath = plugins.path.join(directoryArg, entry.name);
21
+ if (entry.isDirectory()) {
22
+ records.push(...(await readLegacyTree(entryPath, `${prefixArg}/${entry.name}`)));
23
+ }
24
+ else if (entry.isFile()) {
25
+ records.push({
26
+ key: `${prefixArg}/${entry.name}`,
27
+ value: await plugins.fs.readFile(entryPath, 'utf8'),
28
+ });
29
+ }
30
+ }
31
+ return records;
32
+ };
33
+ /**
34
+ * Imports the key/value records that earlier releases kept as files.
35
+ *
36
+ * Both sources are read once and never written back: the ledger, not a renamed file, is
37
+ * what records that this ran. The legacy tree is left on disk untouched so an operator can
38
+ * archive it after confirming the import.
39
+ */
40
+ export const importFilesystemRecords = {
41
+ name: 'import-filesystem-records',
42
+ run: async (contextArg) => {
43
+ // The development-time connections file was the authority over the tree before, and
44
+ // stays it here: it is written first and the tree import below skips existing keys.
45
+ try {
46
+ const legacyText = await plugins.fs.readFile(contextArg.legacyConnectionsFile, 'utf8');
47
+ const legacyConnections = JSON.parse(legacyText);
48
+ for (const connection of legacyConnections) {
49
+ await contextArg.storage.setJSON(`/connections/${connection.id}.json`, connection);
50
+ }
51
+ if (legacyConnections.length > 0) {
52
+ contextArg.log(`imported ${legacyConnections.length} connection(s) from the legacy file`);
53
+ }
54
+ }
55
+ catch (error) {
56
+ if (error.code !== 'ENOENT')
57
+ throw error;
58
+ }
59
+ const records = await readLegacyTree(contextArg.legacyStoragePath, '');
60
+ let imported = 0;
61
+ for (const record of records) {
62
+ // A key that already exists was written by a newer authority — the legacy file above
63
+ // or a release that already ran against the database — so it is never overwritten.
64
+ if (await contextArg.storage.exists(record.key))
65
+ continue;
66
+ await contextArg.storage.set(record.key, record.value);
67
+ imported++;
68
+ }
69
+ if (imported > 0) {
70
+ contextArg.log(`imported ${imported} record(s) from ${contextArg.legacyStoragePath}`);
71
+ }
72
+ },
73
+ };
74
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW1wb3J0LWZpbGVzeXN0ZW0tcmVjb3Jkcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzX21pZ3JhdGlvbnMvaW1wb3J0LWZpbGVzeXN0ZW0tcmVjb3Jkcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGtCQUFrQixDQUFDO0FBTzVDOzs7R0FHRztBQUNILE1BQU0sb0JBQW9CLEdBQUcsS0FBSyxFQUFFLFlBQW9CLEVBQUUsRUFBRTtJQUMxRCxJQUFJLENBQUM7UUFDSCxPQUFPLE1BQU0sT0FBTyxDQUFDLEVBQUUsQ0FBQyxPQUFPLENBQUMsWUFBWSxFQUFFLEVBQUUsYUFBYSxFQUFFLElBQUksRUFBRSxDQUFDLENBQUM7SUFDekUsQ0FBQztJQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7UUFDZixJQUFLLEtBQStCLENBQUMsSUFBSSxLQUFLLFFBQVE7WUFBRSxPQUFPLEVBQUUsQ0FBQztRQUNsRSxNQUFNLEtBQUssQ0FBQztJQUNkLENBQUM7QUFDSCxDQUFDLENBQUM7QUFFRixNQUFNLGNBQWMsR0FBRyxLQUFLLEVBQzFCLFlBQW9CLEVBQ3BCLFNBQWlCLEVBQzBCLEVBQUU7SUFDN0MsTUFBTSxPQUFPLEdBQUcsTUFBTSxvQkFBb0IsQ0FBQyxZQUFZLENBQUMsQ0FBQztJQUN6RCxNQUFNLE9BQU8sR0FBcUMsRUFBRSxDQUFDO0lBQ3JELEtBQUssTUFBTSxLQUFLLElBQUksT0FBTyxFQUFFLENBQUM7UUFDNUIsTUFBTSxTQUFTLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsWUFBWSxFQUFFLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUM5RCxJQUFJLEtBQUssQ0FBQyxXQUFXLEVBQUUsRUFBRSxDQUFDO1lBQ3hCLE9BQU8sQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLE1BQU0sY0FBYyxDQUFDLFNBQVMsRUFBRSxHQUFHLFNBQVMsSUFBSSxLQUFLLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDbkYsQ0FBQzthQUFNLElBQUksS0FBSyxDQUFDLE1BQU0sRUFBRSxFQUFFLENBQUM7WUFDMUIsT0FBTyxDQUFDLElBQUksQ0FBQztnQkFDWCxHQUFHLEVBQUUsR0FBRyxTQUFTLElBQUksS0FBSyxDQUFDLElBQUksRUFBRTtnQkFDakMsS0FBSyxFQUFFLE1BQU0sT0FBTyxDQUFDLEVBQUUsQ0FBQyxRQUFRLENBQUMsU0FBUyxFQUFFLE1BQU0sQ0FBQzthQUNwRCxDQUFDLENBQUM7UUFDTCxDQUFDO0lBQ0gsQ0FBQztJQUNELE9BQU8sT0FBTyxDQUFDO0FBQ2pCLENBQUMsQ0FBQztBQUVGOzs7Ozs7R0FNRztBQUNILE1BQU0sQ0FBQyxNQUFNLHVCQUF1QixHQUFlO0lBQ2pELElBQUksRUFBRSwyQkFBMkI7SUFDakMsR0FBRyxFQUFFLEtBQUssRUFBRSxVQUE2QixFQUFpQixFQUFFO1FBQzFELG9GQUFvRjtRQUNwRixvRkFBb0Y7UUFDcEYsSUFBSSxDQUFDO1lBQ0gsTUFBTSxVQUFVLEdBQUcsTUFBTSxPQUFPLENBQUMsRUFBRSxDQUFDLFFBQVEsQ0FBQyxVQUFVLENBQUMscUJBQXFCLEVBQUUsTUFBTSxDQUFDLENBQUM7WUFDdkYsTUFBTSxpQkFBaUIsR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLFVBQVUsQ0FBd0IsQ0FBQztZQUN4RSxLQUFLLE1BQU0sVUFBVSxJQUFJLGlCQUFpQixFQUFFLENBQUM7Z0JBQzNDLE1BQU0sVUFBVSxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsZ0JBQWdCLFVBQVUsQ0FBQyxFQUFFLE9BQU8sRUFBRSxVQUFVLENBQUMsQ0FBQztZQUNyRixDQUFDO1lBQ0QsSUFBSSxpQkFBaUIsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxFQUFFLENBQUM7Z0JBQ2pDLFVBQVUsQ0FBQyxHQUFHLENBQUMsWUFBWSxpQkFBaUIsQ0FBQyxNQUFNLHFDQUFxQyxDQUFDLENBQUM7WUFDNUYsQ0FBQztRQUNILENBQUM7UUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1lBQ2YsSUFBSyxLQUErQixDQUFDLElBQUksS0FBSyxRQUFRO2dCQUFFLE1BQU0sS0FBSyxDQUFDO1FBQ3RFLENBQUM7UUFFRCxNQUFNLE9BQU8sR0FBRyxNQUFNLGNBQWMsQ0FBQyxVQUFVLENBQUMsaUJBQWlCLEVBQUUsRUFBRSxDQUFDLENBQUM7UUFDdkUsSUFBSSxRQUFRLEdBQUcsQ0FBQyxDQUFDO1FBQ2pCLEtBQUssTUFBTSxNQUFNLElBQUksT0FBTyxFQUFFLENBQUM7WUFDN0IscUZBQXFGO1lBQ3JGLG1GQUFtRjtZQUNuRixJQUFJLE1BQU0sVUFBVSxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQztnQkFBRSxTQUFTO1lBQzFELE1BQU0sVUFBVSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFDLEdBQUcsRUFBRSxNQUFNLENBQUMsS0FBSyxDQUFDLENBQUM7WUFDdkQsUUFBUSxFQUFFLENBQUM7UUFDYixDQUFDO1FBQ0QsSUFBSSxRQUFRLEdBQUcsQ0FBQyxFQUFFLENBQUM7WUFDakIsVUFBVSxDQUFDLEdBQUcsQ0FBQyxZQUFZLFFBQVEsbUJBQW1CLFVBQVUsQ0FBQyxpQkFBaUIsRUFBRSxDQUFDLENBQUM7UUFDeEYsQ0FBQztJQUNILENBQUM7Q0FDRixDQUFDIn0=
@@ -0,0 +1,12 @@
1
+ import type { IMigrationContext } from './interfaces.js';
2
+ export type { IMigration, IMigrationContext } from './interfaces.js';
3
+ export { MigrationRecord } from './classes.migration-record.js';
4
+ /**
5
+ * Runs the pending migrations against an already connected database.
6
+ *
7
+ * Called from startup only, after the database is up and before any manager reads a
8
+ * record. No lease is taken: every migration is idempotent, so two instances starting at
9
+ * the same time converge on the same result, while a lease would let one crashed claimant
10
+ * block every later start.
11
+ */
12
+ export declare const runStartupMigrations: (contextArg: IMigrationContext) => Promise<void>;
@@ -0,0 +1,24 @@
1
+ import { MigrationRecord } from './classes.migration-record.js';
2
+ import { importFilesystemRecords } from './import-filesystem-records.js';
3
+ export { MigrationRecord } from './classes.migration-record.js';
4
+ /** Applied in order; a name that is already in the ledger is skipped. */
5
+ const migrations = [importFilesystemRecords];
6
+ /**
7
+ * Runs the pending migrations against an already connected database.
8
+ *
9
+ * Called from startup only, after the database is up and before any manager reads a
10
+ * record. No lease is taken: every migration is idempotent, so two instances starting at
11
+ * the same time converge on the same result, while a lease would let one crashed claimant
12
+ * block every later start.
13
+ */
14
+ export const runStartupMigrations = async (contextArg) => {
15
+ for (const migration of migrations) {
16
+ if (await MigrationRecord.exists({ migration: migration.name }))
17
+ continue;
18
+ contextArg.log(`running migration ${migration.name}`);
19
+ await migration.run(contextArg);
20
+ await MigrationRecord.atomicUpdate({ migration: migration.name }, { $set: { completedAt: Date.now() } }, { upsert: true });
21
+ contextArg.log(`migration ${migration.name} completed`);
22
+ }
23
+ };
24
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90c19taWdyYXRpb25zL2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSwrQkFBK0IsQ0FBQztBQUNoRSxPQUFPLEVBQUUsdUJBQXVCLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUl6RSxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sK0JBQStCLENBQUM7QUFFaEUseUVBQXlFO0FBQ3pFLE1BQU0sVUFBVSxHQUEwQixDQUFDLHVCQUF1QixDQUFDLENBQUM7QUFFcEU7Ozs7Ozs7R0FPRztBQUNILE1BQU0sQ0FBQyxNQUFNLG9CQUFvQixHQUFHLEtBQUssRUFBRSxVQUE2QixFQUFpQixFQUFFO0lBQ3pGLEtBQUssTUFBTSxTQUFTLElBQUksVUFBVSxFQUFFLENBQUM7UUFDbkMsSUFBSSxNQUFNLGVBQWUsQ0FBQyxNQUFNLENBQWtCLEVBQUUsU0FBUyxFQUFFLFNBQVMsQ0FBQyxJQUFJLEVBQUUsQ0FBQztZQUFFLFNBQVM7UUFDM0YsVUFBVSxDQUFDLEdBQUcsQ0FBQyxxQkFBcUIsU0FBUyxDQUFDLElBQUksRUFBRSxDQUFDLENBQUM7UUFDdEQsTUFBTSxTQUFTLENBQUMsR0FBRyxDQUFDLFVBQVUsQ0FBQyxDQUFDO1FBQ2hDLE1BQU0sZUFBZSxDQUFDLFlBQVksQ0FDaEMsRUFBRSxTQUFTLEVBQUUsU0FBUyxDQUFDLElBQUksRUFBRSxFQUM3QixFQUFFLElBQUksRUFBRSxFQUFFLFdBQVcsRUFBRSxJQUFJLENBQUMsR0FBRyxFQUFFLEVBQUUsRUFBRSxFQUNyQyxFQUFFLE1BQU0sRUFBRSxJQUFJLEVBQUUsQ0FDakIsQ0FBQztRQUNGLFVBQVUsQ0FBQyxHQUFHLENBQUMsYUFBYSxTQUFTLENBQUMsSUFBSSxZQUFZLENBQUMsQ0FBQztJQUMxRCxDQUFDO0FBQ0gsQ0FBQyxDQUFDIn0=
@@ -0,0 +1,16 @@
1
+ import type { StorageManager } from '../dist_ts/storage/index.js';
2
+ export interface IMigrationContext {
3
+ /** The document-backed key/value store every migration writes through. */
4
+ storage: StorageManager;
5
+ /** Root of the key/value tree the releases before the document database wrote. */
6
+ legacyStoragePath: string;
7
+ /** The development-time connection list earlier releases imported on every start. */
8
+ legacyConnectionsFile: string;
9
+ /** Reports progress to the application log. */
10
+ log: (messageArg: string) => void;
11
+ }
12
+ export interface IMigration {
13
+ /** Stable identity recorded in the ledger once the migration completed. */
14
+ name: string;
15
+ run: (contextArg: IMigrationContext) => Promise<void>;
16
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW50ZXJmYWNlcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzX21pZ3JhdGlvbnMvaW50ZXJmYWNlcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiIn0=
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@serve.zone/gitops",
3
- "version": "32.1.0",
3
+ "version": "32.2.0",
4
4
  "description": "GitOps management app for Gitea and GitLab - manage secrets, browse projects, view CI pipelines, and stream build logs",
5
5
  "main": "./dist_ts/index.js",
6
6
  "typings": "./dist_ts/index.d.ts",
@@ -31,10 +31,9 @@
31
31
  "@apiclient.xyz/gitlab": "^2.6.0",
32
32
  "@design.estate/dees-catalog": "3.83.0",
33
33
  "@design.estate/dees-element": "^2.2.4",
34
- "@push.rocks/smartdata": "7.1.7",
34
+ "@lossless.org/client": "1.1.0",
35
35
  "@push.rocks/smartguard": "^3.1.0",
36
36
  "@push.rocks/smartjwt": "^2.2.2",
37
- "@push.rocks/smartmongo": "9.0.0",
38
37
  "@push.rocks/smartsecret": "^1.0.2"
39
38
  },
40
39
  "devDependencies": {
@@ -45,16 +44,19 @@
45
44
  "@git.zone/tsrun": "^2.0.6",
46
45
  "@git.zone/tstest": "6.1.1",
47
46
  "@git.zone/tswatch": "^3.3.5",
47
+ "@push.rocks/smartmongo": "9.0.0",
48
48
  "@types/node": "^26.5.1"
49
49
  },
50
50
  "files": [
51
51
  "ts/**/*",
52
52
  "binary/**/*",
53
53
  "ts_interfaces/**/*",
54
+ "ts_migrations/**/*",
54
55
  "ts_web/**/*",
55
56
  "dist_*/**/*",
56
57
  "dist_ts/**/*",
57
58
  "dist_ts_interfaces/**/*",
59
+ "dist_ts_migrations/**/*",
58
60
  "dist_ts_web/**/*",
59
61
  "dist_serve/**/*",
60
62
  "cli.js",
@@ -71,7 +73,7 @@
71
73
  "start": "(node ./cli.js)",
72
74
  "startTs": "(node ./cli.ts.js)",
73
75
  "test": "(pnpm run build && tstest \"test/*.node.ts\" --verbose --logfile --timeout 60)",
74
- "build": "(tsbuild custom ts_interfaces ts && pnpm run bundle)",
76
+ "build": "(tsbuild custom ts_interfaces ts_migrations ts && pnpm run bundle)",
75
77
  "build:binary": "(pnpm run build && tsdeno compile)",
76
78
  "build:docker": "tsdocker build --verbose",
77
79
  "release:docker": "tsdocker push --verbose",
package/readme.md CHANGED
@@ -11,7 +11,7 @@ For reporting bugs, issues, or security vulnerabilities, please visit [community
11
11
  - Connects to Gitea and GitLab through provider adapters with a shared project, group, secret, pipeline, and file-content interface.
12
12
  - Stores provider tokens and BookStack job tokens through `@push.rocks/smartsecret`, using OS keychain support where available and an encrypted fallback otherwise.
13
13
  - Serves a bundled Lit/dees-catalog single page app through `@api.global/typedserver` with TypedRequest handlers for all backend actions.
14
- - Scans project and group CI/CD secrets into an embedded SmartMongo cache and refreshes that cache on startup and every 24 hours.
14
+ - Scans project and group CI/CD secrets into the document database and refreshes that cache on startup and every 24 hours.
15
15
  - Mirrors repositories from a source connection to a target connection with group offset support, stable source-project mapping for subgroup/path shifts, metadata sync, optional release/asset and OCI image sync, stale target handling, and live sync logs.
16
16
  - Manages reusable secret definitions that can be pushed to selected project or group targets.
17
17
  - Runs scheduled jobs, currently focused on syncing `readme.md`, `changelog.md`, and `docs/*.md` content into BookStack shelves, books, and pages.
@@ -28,24 +28,26 @@ GitOps is a Node.js service packaged as an npm module. Deno is used only for com
28
28
  | Web server | `ts/opsserver/`, TypedServer plus TypedRequest handlers |
29
29
  | Shared contracts | `ts_interfaces/` data and request interfaces |
30
30
  | Frontend | `ts_web/`, Lit web components and smartstate |
31
- | Build output | `dist_ts/`, `dist_ts_interfaces/`, and `dist_serve/` generated by `tsbuild` and `tsbundle` |
32
- | Persistent storage | `~/.serve.zone/gitops/storage/` |
33
- | Cache database | `~/.serve.zone/gitops/tsmdb/` |
31
+ | Build output | `dist_ts/`, `dist_ts_interfaces/`, `dist_ts_migrations/`, and `dist_serve/` generated by `tsbuild` and `tsbundle` |
32
+ | Startup migrations | `ts_migrations/`, applied once against the connected database |
33
+ | Persistence | One document database, reached through `@lossless.org/client/nosqldb` |
34
34
 
35
35
  ## Installation and Usage
36
36
 
37
+ gitops keeps every durable record in one document database, so a MongoDB-compatible server must be reachable before the first start and its URL must be present in `GITOPS_MONGODB_URL`; without it the process refuses by name and exits. An installation upgrading from a release that kept its records under `~/.serve.zone/gitops/storage/` keeps that directory mounted for the first start, which imports the records once.
38
+
37
39
  Install the package through pnpm and start the public `gitops` binary:
38
40
 
39
41
  ```bash
40
42
  pnpm add -g @serve.zone/gitops
41
- GITOPS_PORT=3000 gitops server
43
+ GITOPS_MONGODB_URL=mongodb://localhost:27017 GITOPS_PORT=3000 gitops server
42
44
  ```
43
45
 
44
46
  For local source runs, use the checked-in Node entrypoint:
45
47
 
46
48
  ```bash
47
- node ./cli.js server
48
- GITOPS_PORT=3001 node ./cli.js server
49
+ GITOPS_MONGODB_URL=mongodb://localhost:27017 node ./cli.js server
50
+ GITOPS_MONGODB_URL=mongodb://localhost:27017 GITOPS_PORT=3001 node ./cli.js server
49
51
  ```
50
52
 
51
53
  Container image sync shells out to `skopeo copy --all`; source runs need `skopeo` on `PATH`. The packaged Docker image installs `skopeo` for this path.
@@ -61,10 +63,10 @@ import type * as gitopsInterfaces from '@serve.zone/gitops/interfaces';
61
63
 
62
64
  | Component | Role |
63
65
  | --- | --- |
64
- | `GitopsApp` | Starts storage, provider connections, cache, sync, jobs, managed secrets, scans, and the OpsServer. |
66
+ | `GitopsApp` | Connects the database, applies pending migrations, then starts provider connections, sync, jobs, managed secrets, scans, and the OpsServer. |
65
67
  | `ConnectionManager` | Persists Gitea/GitLab connections, migrates old plaintext tokens into SmartSecret, and background-checks connection health. |
66
68
  | `GiteaProvider` / `GitLabProvider` | Provider-specific adapters behind a shared `BaseProvider` interface. |
67
- | `SecretsScanService` | Fetches project and group secrets in batches and upserts them into cached SmartData documents. |
69
+ | `SecretsScanService` | Fetches project and group secrets in batches and upserts them into `CachedSecret` documents. |
68
70
  | `SyncManager` | Mirrors repositories with bare git clones, stable source/target path mapping, deletion safeguards, branch/default-branch handling, metadata sync, release/asset sync, and OCI image sync. |
69
71
  | `ManagedSecretsManager` | Stores define-once secret records and pushes them to selected project or group targets. |
70
72
  | `JobManager` | Stores scheduled jobs, starts interval timers, masks persisted secrets, and dispatches job execution to registered runners. |
@@ -74,11 +76,15 @@ import type * as gitopsInterfaces from '@serve.zone/gitops/interfaces';
74
76
 
75
77
  | Variable | Default | Purpose |
76
78
  | --- | --- | --- |
79
+ | `GITOPS_MONGODB_URL` | none, required | Connection URL of the document database. Startup fails without it. |
80
+ | `GITOPS_MONGODB_NAME` | `gitops` | Database name inside that server. |
77
81
  | `GITOPS_PORT` | `3000` | HTTP and WebSocket server port. |
78
82
  | `GITOPS_ADMIN_USERNAME` | `admin` | Dashboard login username. |
79
83
  | `GITOPS_ADMIN_PASSWORD` | `admin` | Dashboard login password. |
80
84
 
81
- Provider connections, sync configs, jobs, managed secrets, and action logs are persisted as JSON records below `~/.serve.zone/gitops/storage/`. Cached projects and secrets use a disposable NoSQLDB memory server through SmartMongo 9. The initial provider scan repopulates this cache after startup; it does not persist between processes.
85
+ Provider connections, sync configs, jobs, managed secrets, and action logs are persisted as `StorageRecord` documents, keyed by the same `/connections/<id>.json`-style keys earlier releases used as file names. Cached projects and secrets are `CachedProject` and `CachedSecret` documents in the same database, bounded by their TTL and swept hourly by the cache cleaner.
86
+
87
+ A release that finds records below `~/.serve.zone/gitops/storage/` imports them once, on the first start against the database, and records that in the migration ledger. The directory is read, never written, and is left in place so it can be archived after the import is confirmed.
82
88
 
83
89
  Provider connections may also define `registryUrl`, `registryUsername`, and `registryToken` for OCI registry mirroring. Registry tokens are stored through SmartSecret and masked in API/UI responses. GitLab image sync falls back to `oauth2:<provider token>` when no dedicated registry credentials are configured; Gitea requires an explicit registry username when registry auth is needed.
84
90
 
@@ -183,11 +189,11 @@ pnpm test
183
189
  pnpm run startTs
184
190
  ```
185
191
 
186
- Useful direct commands:
192
+ Useful direct commands (each server start needs `GITOPS_MONGODB_URL`):
187
193
 
188
194
  ```bash
189
- node ./cli.js server
190
- GITOPS_PORT=3001 node ./cli.js server
195
+ GITOPS_MONGODB_URL=mongodb://localhost:27017 node ./cli.js server
196
+ GITOPS_MONGODB_URL=mongodb://localhost:27017 GITOPS_PORT=3001 node ./cli.js server
191
197
  pnpm run build:binary
192
198
  pnpm run build:docker
193
199
  pnpm run watch
@@ -206,11 +212,12 @@ gitops/
206
212
  ├── html/
207
213
  ├── ts/
208
214
  │ ├── classes/
209
- │ ├── cache/
215
+ │ ├── db/
210
216
  │ ├── opsserver/
211
217
  │ ├── providers/
212
218
  │ └── storage/
213
219
  ├── ts_interfaces/
220
+ ├── ts_migrations/
214
221
  ├── ts_web/
215
222
  ├── dist_serve/
216
223
  └── test/
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@serve.zone/gitops',
6
- version: '32.1.0',
6
+ version: '32.2.0',
7
7
  description: 'GitOps management app for Gitea and GitLab - manage secrets, browse projects, view CI pipelines, and stream build logs'
8
8
  }
@@ -6,7 +6,7 @@ const ACTIONLOG_PREFIX = '/actionlog/';
6
6
 
7
7
  /**
8
8
  * Persists and queries action log entries via StorageManager.
9
- * Entries are stored as individual JSON files keyed by timestamp-id.
9
+ * Entries are stored as individual records keyed by timestamp-id.
10
10
  */
11
11
  export class ActionLog {
12
12
  private storageManager: StorageManager;
@@ -4,7 +4,6 @@ import type * as interfaces from '../../ts_interfaces/index.js';
4
4
  import { BaseProvider, GiteaProvider, GitLabProvider } from '../providers/index.js';
5
5
  import type { StorageManager } from '../storage/index.js';
6
6
 
7
- const LEGACY_CONNECTIONS_FILE = './.nogit/connections.json';
8
7
  const CONNECTIONS_PREFIX = '/connections/';
9
8
  const KEYCHAIN_PREFIX = 'keychain:';
10
9
 
@@ -19,7 +18,7 @@ const mintWebhookSecret = (): string => plugins.crypto.randomBytes(32).toString(
19
18
 
20
19
  /**
21
20
  * Manages provider connections — persists each connection as an
22
- * individual JSON file via StorageManager. Tokens are stored in
21
+ * individual record via StorageManager. Tokens are stored in
23
22
  * the OS keychain (or encrypted file fallback) via SmartSecret.
24
23
  */
25
24
  export class ConnectionManager {
@@ -35,7 +34,6 @@ export class ConnectionManager {
35
34
  }
36
35
 
37
36
  async init(): Promise<void> {
38
- await this.migrateLegacyFile();
39
37
  await this.loadConnections();
40
38
  // Auto-test all connections in the background
41
39
  this.healthCheckDone = this.testAllConnections();
@@ -59,27 +57,6 @@ export class ConnectionManager {
59
57
  }
60
58
  }
61
59
 
62
- /**
63
- * One-time migration from the legacy .nogit/connections.json file.
64
- */
65
- private async migrateLegacyFile(): Promise<void> {
66
- try {
67
- const text = await plugins.fs.readFile(LEGACY_CONNECTIONS_FILE, 'utf8');
68
- const legacy: interfaces.data.IProviderConnection[] = JSON.parse(text);
69
- if (legacy.length > 0) {
70
- logger.info(`Migrating ${legacy.length} connection(s) from legacy file...`);
71
- for (const conn of legacy) {
72
- await this.storageManager.setJSON(`${CONNECTIONS_PREFIX}${conn.id}.json`, conn);
73
- }
74
- // Rename legacy file so migration doesn't repeat
75
- await plugins.fs.rename(LEGACY_CONNECTIONS_FILE, LEGACY_CONNECTIONS_FILE + '.migrated');
76
- logger.success('Legacy connections migrated successfully');
77
- }
78
- } catch {
79
- // No legacy file or already migrated — nothing to do
80
- }
81
- }
82
-
83
60
  private async loadConnections(): Promise<void> {
84
61
  const keys = await this.storageManager.list(CONNECTIONS_PREFIX);
85
62
  this.connections = [];
@@ -156,7 +133,7 @@ export class ConnectionManager {
156
133
  ): Promise<void> {
157
134
  try {
158
135
  await this.smartSecret.setSecret(conn.id, conn.token);
159
- // Save sentinel to JSON file
136
+ // Save sentinel to the stored record
160
137
  const jsonConn = { ...conn, token: `${KEYCHAIN_PREFIX}${conn.id}` };
161
138
  await this.storageManager.setJSON(`${CONNECTIONS_PREFIX}${conn.id}.json`, jsonConn);
162
139
  logger.info(`Migrated token for connection "${conn.name}" to keychain`);
@@ -193,7 +170,7 @@ export class ConnectionManager {
193
170
  private async backfillWebhookSecret(conn: interfaces.data.IProviderConnection): Promise<void> {
194
171
  // A stored-but-unreadable secret is not a missing one. Minting here would write a new
195
172
  // secret over the same keychain id and destroy confirmed operator material because of
196
- // a transient read failure, so this connection is left exactly as it is on disk.
173
+ // a transient read failure, so this connection is left exactly as it is stored.
197
174
  if (conn.webhookSecretUnreadable) return;
198
175
  if (conn.webhookSecret && conn.webhookStatus) return;
199
176
  if (!conn.webhookSecret) {
@@ -226,7 +203,7 @@ export class ConnectionManager {
226
203
  } else {
227
204
  await this.smartSecret.deleteSecret(getWebhookSecretId(conn.id));
228
205
  }
229
- // Save JSON with sentinel value
206
+ // Save the record with sentinel values
230
207
  const jsonConn = {
231
208
  ...conn,
232
209
  token: `${KEYCHAIN_PREFIX}${conn.id}`,
@@ -7,10 +7,25 @@ import { ManagedSecretsManager } from './managedsecrets.manager.js';
7
7
  import { JobManager } from './jobmanager.js';
8
8
  import { OpsServer } from '../opsserver/index.js';
9
9
  import { StorageManager } from '../storage/index.js';
10
- import { CacheDb, CacheCleaner, CachedProject, CachedSecret, SecretsScanService } from '../cache/index.js';
10
+ import { SecretsScanService } from './secrets.scan.service.js';
11
+ import {
12
+ CacheCleaner,
13
+ CachedProject,
14
+ CachedSecret,
15
+ GitopsDb,
16
+ readGitopsDbOptionsFromEnvironment,
17
+ type IGitopsDbOptions,
18
+ } from '../db/index.js';
11
19
  import { resolvePaths } from '../paths.js';
20
+ import { runStartupMigrations } from '../../ts_migrations/index.js';
12
21
  import { unrefTimer } from '../timers.js';
13
22
 
23
+ /**
24
+ * The connection list a development checkout kept next to itself before gitops had a
25
+ * database. Resolved against the working directory, exactly as the release that wrote it did.
26
+ */
27
+ const legacyConnectionsFile = './.nogit/connections.json';
28
+
14
29
  /**
15
30
  * Main GitOps application orchestrator
16
31
  */
@@ -20,7 +35,7 @@ export class GitopsApp {
20
35
  public connectionManager: ConnectionManager;
21
36
  public actionLog: ActionLog;
22
37
  public opsServer: OpsServer;
23
- public cacheDb: CacheDb;
38
+ public db: GitopsDb;
24
39
  public cacheCleaner: CacheCleaner;
25
40
  public syncManager!: SyncManager;
26
41
  public managedSecretsManager!: ManagedSecretsManager;
@@ -29,22 +44,19 @@ export class GitopsApp {
29
44
  private scanIntervalId: ReturnType<typeof setInterval> | null = null;
30
45
  private paths: ReturnType<typeof resolvePaths>;
31
46
 
32
- constructor() {
33
- const paths = resolvePaths();
34
- this.paths = paths;
35
- this.storageManager = new StorageManager({
36
- backend: 'filesystem',
37
- fsPath: paths.defaultStoragePath,
38
- });
47
+ /**
48
+ * @param dbOptions binding of the document database; read from the environment unless a
49
+ * caller supplies one, which is how tests bind an isolated server.
50
+ */
51
+ constructor(dbOptions: IGitopsDbOptions = readGitopsDbOptionsFromEnvironment()) {
52
+ this.paths = resolvePaths();
53
+ this.storageManager = new StorageManager();
39
54
  this.smartSecret = new plugins.smartsecret.SmartSecret({ service: 'gitops' });
40
55
  this.connectionManager = new ConnectionManager(this.storageManager, this.smartSecret);
41
56
  this.actionLog = new ActionLog(this.storageManager);
42
57
 
43
- this.cacheDb = CacheDb.getInstance({
44
- storagePath: paths.defaultTsmDbPath,
45
- dbName: 'gitops_cache',
46
- });
47
- this.cacheCleaner = new CacheCleaner(this.cacheDb);
58
+ this.db = GitopsDb.getInstance(dbOptions);
59
+ this.cacheCleaner = new CacheCleaner();
48
60
  this.cacheCleaner.registerClass(CachedProject);
49
61
  this.cacheCleaner.registerClass(CachedSecret);
50
62
 
@@ -54,8 +66,15 @@ export class GitopsApp {
54
66
  async start(port = 3000): Promise<void> {
55
67
  logger.info('Initializing GitOps...');
56
68
 
57
- // Start CacheDb
58
- await this.cacheDb.start();
69
+ // Every record below is read through the database, so it comes up first and the
70
+ // pending migrations run before any manager looks at a key.
71
+ await this.db.start();
72
+ await runStartupMigrations({
73
+ storage: this.storageManager,
74
+ legacyStoragePath: this.paths.legacyStoragePath,
75
+ legacyConnectionsFile: legacyConnectionsFile,
76
+ log: (messageArg) => logger.info(messageArg),
77
+ });
59
78
 
60
79
  // Initialize connection manager (loads saved connections)
61
80
  await this.connectionManager.init();
@@ -122,7 +141,7 @@ export class GitopsApp {
122
141
  await this.syncManager.stop();
123
142
  }
124
143
  this.cacheCleaner.stop();
125
- await this.cacheDb.stop();
144
+ await this.db.stop();
126
145
  logger.success('GitOps shutdown complete');
127
146
  }
128
147
  }
@@ -1,7 +1,7 @@
1
1
  import { logger } from '../logging.js';
2
- import type { ConnectionManager } from '../classes/connectionmanager.js';
3
- import { CachedSecret } from './documents/classes.cached.secret.js';
4
- import { TTL } from './classes.cached.document.js';
2
+ import type { ConnectionManager } from './connectionmanager.js';
3
+ import { CachedSecret } from '../db/documents/classes.cached.secret.js';
4
+ import { TTL } from '../db/classes.cached.document.js';
5
5
  import type { ISecret } from '../../ts_interfaces/data/secret.js';
6
6
 
7
7
  export interface IScanResult {
@@ -1,6 +1,5 @@
1
1
  import { logger } from '../logging.js';
2
2
  import { unrefTimer } from '../timers.js';
3
- import type { CacheDb } from './classes.cachedb.js';
4
3
 
5
4
  // deno-lint-ignore no-explicit-any
6
5
  type DocumentClass = { getInstances: (filter: any) => Promise<{ delete: () => Promise<void> }[]> };
@@ -14,10 +13,8 @@ export class CacheCleaner {
14
13
  private intervalId: ReturnType<typeof setInterval> | null = null;
15
14
  private intervalMs: number;
16
15
  private documentClasses: DocumentClass[] = [];
17
- private cacheDb: CacheDb;
18
16
 
19
- constructor(cacheDb: CacheDb, intervalMs = DEFAULT_INTERVAL_MS) {
20
- this.cacheDb = cacheDb;
17
+ constructor(intervalMs = DEFAULT_INTERVAL_MS) {
21
18
  this.intervalMs = intervalMs;
22
19
  }
23
20
 
@@ -12,7 +12,7 @@ export const TTL = {
12
12
 
13
13
  /**
14
14
  * Abstract base class for cached documents with TTL support.
15
- * Extend this class and add @Collection decorator pointing to your CacheDb.
15
+ * Extend this class and add a @Collection decorator bound to GitopsDb.
16
16
  */
17
17
  export abstract class CachedDocument<
18
18
  T extends CachedDocument<T>,