@camstack/core 0.1.13 → 0.1.15
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.
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +220 -0
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js.map +1 -0
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +9 -0
- package/dist/builtins/addon-pages-aggregator/index.js +222 -0
- package/dist/builtins/addon-pages-aggregator/index.js.map +1 -0
- package/dist/builtins/addon-pages-aggregator/index.mjs +9 -0
- package/dist/builtins/addon-pages-aggregator/index.mjs.map +1 -0
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +200 -0
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js.map +1 -0
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +9 -0
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs.map +1 -0
- package/dist/builtins/addon-widgets-aggregator/index.js +202 -0
- package/dist/builtins/addon-widgets-aggregator/index.js.map +1 -0
- package/dist/builtins/addon-widgets-aggregator/index.mjs +9 -0
- package/dist/builtins/addon-widgets-aggregator/index.mjs.map +1 -0
- package/dist/builtins/alerts/alerts.addon.js +443 -0
- package/dist/builtins/alerts/alerts.addon.js.map +1 -0
- package/dist/builtins/alerts/alerts.addon.mjs +9 -0
- package/dist/builtins/alerts/alerts.addon.mjs.map +1 -0
- package/dist/builtins/alerts/index.js +443 -0
- package/dist/builtins/alerts/index.js.map +1 -0
- package/dist/builtins/alerts/index.mjs +8 -0
- package/dist/builtins/alerts/index.mjs.map +1 -0
- package/dist/builtins/console-logging/index.js +242 -0
- package/dist/builtins/console-logging/index.js.map +1 -0
- package/dist/builtins/console-logging/index.mjs +11 -0
- package/dist/builtins/console-logging/index.mjs.map +1 -0
- package/dist/builtins/device-manager/device-manager.addon.js +2155 -0
- package/dist/builtins/device-manager/device-manager.addon.js.map +1 -0
- package/dist/builtins/device-manager/device-manager.addon.mjs +9 -0
- package/dist/builtins/device-manager/device-manager.addon.mjs.map +1 -0
- package/dist/builtins/device-manager/index.js +2157 -0
- package/dist/builtins/device-manager/index.js.map +1 -0
- package/dist/builtins/device-manager/index.mjs +10 -0
- package/dist/builtins/device-manager/index.mjs.map +1 -0
- package/dist/builtins/hub-forwarder/index.js +297 -0
- package/dist/builtins/hub-forwarder/index.js.map +1 -0
- package/dist/builtins/hub-forwarder/index.mjs +11 -0
- package/dist/builtins/hub-forwarder/index.mjs.map +1 -0
- package/dist/builtins/local-auth/index.js +623 -0
- package/dist/builtins/local-auth/index.js.map +1 -0
- package/dist/builtins/local-auth/index.mjs +8 -0
- package/dist/builtins/local-auth/index.mjs.map +1 -0
- package/dist/builtins/local-auth/local-auth.addon.js +623 -0
- package/dist/builtins/local-auth/local-auth.addon.js.map +1 -0
- package/dist/builtins/local-auth/local-auth.addon.mjs +9 -0
- package/dist/builtins/local-auth/local-auth.addon.mjs.map +1 -0
- package/dist/builtins/local-backup/index.js +53 -68
- package/dist/builtins/local-backup/index.js.map +1 -1
- package/dist/builtins/local-backup/index.mjs +1 -1
- package/dist/builtins/native-metrics/native-metrics.addon.js +898 -0
- package/dist/builtins/native-metrics/native-metrics.addon.js.map +1 -0
- package/dist/builtins/native-metrics/native-metrics.addon.mjs +7 -0
- package/dist/builtins/native-metrics/native-metrics.addon.mjs.map +1 -0
- package/dist/builtins/snapshot/index.js +504 -0
- package/dist/builtins/snapshot/index.js.map +1 -0
- package/dist/builtins/snapshot/index.mjs +477 -0
- package/dist/builtins/snapshot/index.mjs.map +1 -0
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +16 -166
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.js.map +1 -1
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
- package/dist/builtins/sqlite-storage/index.js +554 -621
- package/dist/builtins/sqlite-storage/index.js.map +1 -1
- package/dist/builtins/sqlite-storage/index.mjs +9 -11
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +368 -130
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.js.map +1 -1
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +1 -1
- package/dist/builtins/system-config/index.js +189 -0
- package/dist/builtins/system-config/index.js.map +1 -0
- package/dist/builtins/system-config/index.mjs +10 -0
- package/dist/builtins/system-config/index.mjs.map +1 -0
- package/dist/builtins/system-config/system-config.addon.js +187 -0
- package/dist/builtins/system-config/system-config.addon.js.map +1 -0
- package/dist/builtins/system-config/system-config.addon.mjs +9 -0
- package/dist/builtins/system-config/system-config.addon.mjs.map +1 -0
- package/dist/builtins/winston-logging/index.js +185 -65
- package/dist/builtins/winston-logging/index.js.map +1 -1
- package/dist/builtins/winston-logging/index.mjs +2 -1
- package/dist/chunk-2CIYKDRN.mjs +1 -0
- package/dist/chunk-2CIYKDRN.mjs.map +1 -0
- package/dist/chunk-2F76X6NL.mjs +136 -0
- package/dist/chunk-2F76X6NL.mjs.map +1 -0
- package/dist/chunk-2QUFBZ7M.mjs +1 -0
- package/dist/chunk-2QUFBZ7M.mjs.map +1 -0
- package/dist/chunk-3BK2Y7GY.mjs +593 -0
- package/dist/chunk-3BK2Y7GY.mjs.map +1 -0
- package/dist/chunk-4OOHFJHT.mjs +421 -0
- package/dist/chunk-4OOHFJHT.mjs.map +1 -0
- package/dist/chunk-4XHB7IHT.mjs +809 -0
- package/dist/chunk-4XHB7IHT.mjs.map +1 -0
- package/dist/{chunk-2F3XZYRW.mjs → chunk-6M2HSSTQ.mjs} +16 -7
- package/dist/chunk-6M2HSSTQ.mjs.map +1 -0
- package/dist/{chunk-SO4LROOT.mjs → chunk-7FI7SQS7.mjs} +54 -69
- package/dist/chunk-7FI7SQS7.mjs.map +1 -0
- package/dist/chunk-ED57RCQE.mjs +171 -0
- package/dist/chunk-ED57RCQE.mjs.map +1 -0
- package/dist/chunk-FZN56HGQ.mjs +626 -0
- package/dist/chunk-FZN56HGQ.mjs.map +1 -0
- package/dist/chunk-GL4OOB25.mjs +51 -0
- package/dist/chunk-GL4OOB25.mjs.map +1 -0
- package/dist/chunk-KDG2NTDB.mjs +137 -0
- package/dist/chunk-KDG2NTDB.mjs.map +1 -0
- package/dist/chunk-NRBQWBDM.mjs +191 -0
- package/dist/chunk-NRBQWBDM.mjs.map +1 -0
- package/dist/chunk-O4V246GG.mjs +2137 -0
- package/dist/chunk-O4V246GG.mjs.map +1 -0
- package/dist/chunk-QT57H266.mjs +163 -0
- package/dist/chunk-QT57H266.mjs.map +1 -0
- package/dist/chunk-QX4RH25I.mjs +141 -0
- package/dist/chunk-QX4RH25I.mjs.map +1 -0
- package/dist/chunk-TB562PZX.mjs +86 -0
- package/dist/chunk-TB562PZX.mjs.map +1 -0
- package/dist/chunk-TDYPZXK5.mjs +1 -0
- package/dist/chunk-TDYPZXK5.mjs.map +1 -0
- package/dist/chunk-UJI4LN5P.mjs +36 -0
- package/dist/chunk-UJI4LN5P.mjs.map +1 -0
- package/dist/chunk-W6RTHQGP.mjs +1 -0
- package/dist/chunk-W6RTHQGP.mjs.map +1 -0
- package/dist/chunk-ZELBCPDC.mjs +369 -0
- package/dist/chunk-ZELBCPDC.mjs.map +1 -0
- package/dist/index.d.mts +1103 -544
- package/dist/index.d.ts +1103 -544
- package/dist/index.js +7032 -6033
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +568 -2226
- package/dist/index.mjs.map +1 -1
- package/dist/resource-monitor-UZUGPIAU.mjs +9 -0
- package/dist/resource-monitor-UZUGPIAU.mjs.map +1 -0
- package/dist/storage-location-manager-HFNB3PCS.mjs +7 -0
- package/dist/storage-location-manager-HFNB3PCS.mjs.map +1 -0
- package/package.json +123 -2
- package/dist/builtins/local-backup/index.d.mts +0 -42
- package/dist/builtins/local-backup/index.d.ts +0 -42
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.d.mts +0 -2
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.d.ts +0 -2
- package/dist/builtins/sqlite-storage/index.d.mts +0 -4
- package/dist/builtins/sqlite-storage/index.d.ts +0 -4
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.d.mts +0 -2
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.d.ts +0 -2
- package/dist/builtins/winston-logging/index.d.mts +0 -30
- package/dist/builtins/winston-logging/index.d.ts +0 -30
- package/dist/chunk-2F3XZYRW.mjs.map +0 -1
- package/dist/chunk-LQFPAEQF.mjs +0 -147
- package/dist/chunk-LQFPAEQF.mjs.map +0 -1
- package/dist/chunk-R3DIIBBX.mjs +0 -532
- package/dist/chunk-R3DIIBBX.mjs.map +0 -1
- package/dist/chunk-SMNR44VG.mjs +0 -386
- package/dist/chunk-SMNR44VG.mjs.map +0 -1
- package/dist/chunk-SO4LROOT.mjs.map +0 -1
- package/dist/chunk-SPA4JBKN.mjs +0 -175
- package/dist/chunk-SPA4JBKN.mjs.map +0 -1
- package/dist/dist-3BY63UQ5.mjs +0 -2151
- package/dist/dist-3BY63UQ5.mjs.map +0 -1
- package/dist/filesystem-storage.addon-C42r589X.d.mts +0 -57
- package/dist/filesystem-storage.addon-C42r589X.d.ts +0 -57
- package/dist/sql-schema-CKz78rId.d.mts +0 -97
- package/dist/sql-schema-CKz78rId.d.ts +0 -97
- package/dist/sqlite-settings.addon-KwG-uKMP.d.mts +0 -79
- package/dist/sqlite-settings.addon-KwG-uKMP.d.ts +0 -79
- package/dist/storage-location-manager-KKDQNAKA.mjs +0 -7
- /package/dist/{storage-location-manager-KKDQNAKA.mjs.map → builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs.map} +0 -0
|
@@ -34,19 +34,50 @@ __export(sqlite_settings_addon_exports, {
|
|
|
34
34
|
default: () => sqlite_settings_addon_default
|
|
35
35
|
});
|
|
36
36
|
module.exports = __toCommonJS(sqlite_settings_addon_exports);
|
|
37
|
+
var import_types2 = require("@camstack/types");
|
|
37
38
|
|
|
38
39
|
// src/builtins/sqlite-storage/sqlite-settings-backend.ts
|
|
39
40
|
var import_better_sqlite3 = __toESM(require("better-sqlite3"));
|
|
40
41
|
var import_node_crypto = require("crypto");
|
|
41
|
-
var
|
|
42
|
+
var import_types = require("@camstack/types");
|
|
43
|
+
function parseRowData(raw) {
|
|
44
|
+
return (0, import_types.asJsonObject)((0, import_types.parseJsonUnknown)(raw)) ?? {};
|
|
45
|
+
}
|
|
46
|
+
var SqliteSettingsBackend = class _SqliteSettingsBackend {
|
|
47
|
+
// Domain schemas (auth, analytics events, …) live in the addons that
|
|
48
|
+
// own them — same pattern as `pipeline-analytics` (`event-store.ts`,
|
|
49
|
+
// `track-store.ts`, `media-store.ts`) and `local-auth` (`auth-schema.ts`).
|
|
50
|
+
// The backend exposes `declareCollection` and stays out of domain
|
|
51
|
+
// knowledge.
|
|
42
52
|
constructor(dbPath, runtimeDefaults) {
|
|
43
53
|
this.dbPath = dbPath;
|
|
44
54
|
this.runtimeDefaults = runtimeDefaults ?? {};
|
|
45
55
|
}
|
|
56
|
+
dbPath;
|
|
46
57
|
db = null;
|
|
47
|
-
ensuredTables = /* @__PURE__ */ new Set();
|
|
48
58
|
structuredTables = /* @__PURE__ */ new Set();
|
|
59
|
+
/** Map from scoped collection name → set of column names (non-id) that
|
|
60
|
+
* the structured schema owns. Routes set/get/insert/update/query to
|
|
61
|
+
* typed columns. Every collection MUST be declared here before use. */
|
|
62
|
+
declaredCollections = /* @__PURE__ */ new Map();
|
|
49
63
|
runtimeDefaults;
|
|
64
|
+
/**
|
|
65
|
+
* Canonical key/value collections — declared with a `(id TEXT PK,
|
|
66
|
+
* data TEXT NOT NULL)` schema at boot so existing JSON-blob rows
|
|
67
|
+
* keep working through the structured path. Generates SQL identical
|
|
68
|
+
* to the previous legacy path; only the routing is unified.
|
|
69
|
+
*/
|
|
70
|
+
static CANONICAL_KV_COLLECTIONS = [
|
|
71
|
+
"system-settings",
|
|
72
|
+
"addon-settings",
|
|
73
|
+
"addon-device-settings",
|
|
74
|
+
"addon-devices",
|
|
75
|
+
"device-runtime-state",
|
|
76
|
+
"sections",
|
|
77
|
+
"provider-settings",
|
|
78
|
+
"device-settings",
|
|
79
|
+
"alerts"
|
|
80
|
+
];
|
|
50
81
|
async initialize() {
|
|
51
82
|
const dir = this.dbPath.substring(0, this.dbPath.lastIndexOf("/"));
|
|
52
83
|
if (dir) {
|
|
@@ -56,11 +87,30 @@ var SqliteSettingsBackend = class {
|
|
|
56
87
|
this.db = new import_better_sqlite3.default(this.dbPath);
|
|
57
88
|
this.db.pragma("journal_mode = WAL");
|
|
58
89
|
this.db.pragma("foreign_keys = ON");
|
|
59
|
-
const
|
|
90
|
+
for (const collection of _SqliteSettingsBackend.CANONICAL_KV_COLLECTIONS) {
|
|
91
|
+
await this.ensureTable(collection, {
|
|
92
|
+
columns: [
|
|
93
|
+
{ name: "id", type: "TEXT", primaryKey: true, notNull: true },
|
|
94
|
+
{ name: "data", type: "TEXT", notNull: true }
|
|
95
|
+
]
|
|
96
|
+
});
|
|
97
|
+
this.declaredCollections.set(collection, {
|
|
98
|
+
primaryKey: "id",
|
|
99
|
+
columns: /* @__PURE__ */ new Set(["data"])
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
const isEmpty = await this.isEmpty({ collection: "system-settings" });
|
|
60
103
|
if (isEmpty) {
|
|
61
104
|
await this.seedDefaults();
|
|
62
105
|
}
|
|
63
106
|
}
|
|
107
|
+
requireDeclared(scoped) {
|
|
108
|
+
const decl = this.declaredCollections.get(scoped);
|
|
109
|
+
if (!decl) {
|
|
110
|
+
throw new Error(`SqliteSettingsBackend: collection "${scoped}" is not declared. Call declareCollection() first or add it to CANONICAL_KV_COLLECTIONS.`);
|
|
111
|
+
}
|
|
112
|
+
return decl;
|
|
113
|
+
}
|
|
64
114
|
async shutdown() {
|
|
65
115
|
this.db?.close();
|
|
66
116
|
this.db = null;
|
|
@@ -68,148 +118,255 @@ var SqliteSettingsBackend = class {
|
|
|
68
118
|
// ---------------------------------------------------------------------------
|
|
69
119
|
// ISettingsBackend implementation
|
|
70
120
|
// ---------------------------------------------------------------------------
|
|
71
|
-
async get(collection, key) {
|
|
72
|
-
this.
|
|
73
|
-
const
|
|
121
|
+
async get({ namespace, collection, key }) {
|
|
122
|
+
const scoped = this.scopedName(namespace, collection);
|
|
123
|
+
const decl = this.requireDeclared(scoped);
|
|
124
|
+
const cols = [`"${decl.primaryKey}"`, ...[...decl.columns].map((c) => `"${c}"`)].join(", ");
|
|
125
|
+
const row = this.getDb().prepare(`SELECT ${cols} FROM "${scoped}" WHERE "${decl.primaryKey}" = ?`).get(key);
|
|
74
126
|
if (!row) return void 0;
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
127
|
+
if (decl.columns.size === 1 && decl.columns.has("data")) {
|
|
128
|
+
const raw = row["data"];
|
|
129
|
+
return typeof raw === "string" ? JSON.parse(raw) : raw;
|
|
130
|
+
}
|
|
131
|
+
const data = {};
|
|
132
|
+
for (const c of decl.columns) {
|
|
133
|
+
const v = row[c];
|
|
134
|
+
if (typeof v === "string" && (v.startsWith("{") || v.startsWith("["))) {
|
|
135
|
+
try {
|
|
136
|
+
data[c] = JSON.parse(v);
|
|
137
|
+
} catch {
|
|
138
|
+
data[c] = v;
|
|
139
|
+
}
|
|
140
|
+
} else {
|
|
141
|
+
data[c] = v ?? null;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return data;
|
|
145
|
+
}
|
|
146
|
+
async set({ namespace, collection, key, value }) {
|
|
147
|
+
const scoped = this.scopedName(namespace, collection);
|
|
148
|
+
const decl = this.requireDeclared(scoped);
|
|
149
|
+
const row = { [decl.primaryKey]: key };
|
|
150
|
+
if (decl.columns.size === 1 && decl.columns.has("data")) {
|
|
151
|
+
row["data"] = JSON.stringify(value);
|
|
152
|
+
} else {
|
|
153
|
+
const valueObj = value !== null && typeof value === "object" ? value : {};
|
|
154
|
+
for (const [k, v] of Object.entries(valueObj)) {
|
|
155
|
+
if (decl.columns.has(k)) row[k] = this.serializeColumnValue(v);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
const keys = Object.keys(row);
|
|
159
|
+
const cols = keys.map((k) => `"${k}"`).join(", ");
|
|
160
|
+
const placeholders = keys.map(() => "?").join(", ");
|
|
161
|
+
const updates = keys.filter((k) => k !== decl.primaryKey).map((k) => `"${k}" = excluded."${k}"`).join(", ");
|
|
162
|
+
const values = keys.map((k) => row[k]);
|
|
163
|
+
const sql = updates.length > 0 ? `INSERT INTO "${scoped}" (${cols}) VALUES (${placeholders}) ON CONFLICT("${decl.primaryKey}") DO UPDATE SET ${updates}` : `INSERT INTO "${scoped}" (${cols}) VALUES (${placeholders}) ON CONFLICT("${decl.primaryKey}") DO NOTHING`;
|
|
164
|
+
this.getDb().prepare(sql).run(...values);
|
|
165
|
+
}
|
|
166
|
+
async query({ namespace, collection, filter }) {
|
|
167
|
+
const scoped = this.scopedName(namespace, collection);
|
|
168
|
+
const decl = this.requireDeclared(scoped);
|
|
169
|
+
return this.queryDeclared(scoped, decl, filter);
|
|
170
|
+
}
|
|
171
|
+
async insert({ namespace, collection, record }) {
|
|
172
|
+
const scoped = this.scopedName(namespace, collection);
|
|
173
|
+
const decl = this.requireDeclared(scoped);
|
|
174
|
+
const id = record.id || (0, import_node_crypto.randomUUID)();
|
|
175
|
+
const row = { [decl.primaryKey]: id };
|
|
176
|
+
if (decl.columns.size === 1 && decl.columns.has("data")) {
|
|
177
|
+
row["data"] = JSON.stringify(record.data);
|
|
178
|
+
} else {
|
|
179
|
+
for (const [k, v] of Object.entries(record.data)) {
|
|
180
|
+
if (decl.columns.has(k)) row[k] = this.serializeColumnValue(v);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
await this.tableInsert(scoped, row);
|
|
184
|
+
}
|
|
185
|
+
async update({ namespace, collection, id, data }) {
|
|
186
|
+
const scoped = this.scopedName(namespace, collection);
|
|
187
|
+
const decl = this.requireDeclared(scoped);
|
|
188
|
+
const updates = {};
|
|
189
|
+
if (decl.columns.size === 1 && decl.columns.has("data")) {
|
|
190
|
+
updates["data"] = JSON.stringify(data);
|
|
191
|
+
} else {
|
|
192
|
+
for (const [k, v] of Object.entries(data)) {
|
|
193
|
+
if (decl.columns.has(k)) updates[k] = this.serializeColumnValue(v);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
if (Object.keys(updates).length > 0) {
|
|
197
|
+
await this.tableUpdate(scoped, { [decl.primaryKey]: id }, updates);
|
|
198
|
+
}
|
|
80
199
|
}
|
|
81
|
-
async
|
|
82
|
-
this.
|
|
83
|
-
|
|
200
|
+
async delete({ namespace, collection, key }) {
|
|
201
|
+
const scoped = this.scopedName(namespace, collection);
|
|
202
|
+
const decl = this.requireDeclared(scoped);
|
|
203
|
+
await this.tableDelete(scoped, { [decl.primaryKey]: key });
|
|
204
|
+
}
|
|
205
|
+
async count({ namespace, collection, filter }) {
|
|
206
|
+
const scoped = this.scopedName(namespace, collection);
|
|
207
|
+
const decl = this.requireDeclared(scoped);
|
|
208
|
+
const isKvShape = decl.columns.size === 1 && decl.columns.has("data");
|
|
209
|
+
const isColumn = (f) => f === decl.primaryKey || decl.columns.has(f);
|
|
210
|
+
const fieldExpr = (f) => {
|
|
211
|
+
if (isColumn(f)) return `"${f}"`;
|
|
212
|
+
if (isKvShape) return `json_extract("data", '$.${f}')`;
|
|
213
|
+
return "";
|
|
214
|
+
};
|
|
215
|
+
let sql = `SELECT COUNT(*) AS cnt FROM "${scoped}"`;
|
|
216
|
+
const params = [];
|
|
217
|
+
if (filter?.where) {
|
|
218
|
+
const clauses = [];
|
|
219
|
+
for (const [field, value] of Object.entries(filter.where)) {
|
|
220
|
+
const expr = fieldExpr(field);
|
|
221
|
+
if (!expr) continue;
|
|
222
|
+
clauses.push(`${expr} = ?`);
|
|
223
|
+
params.push(this.serializeColumnValue(value));
|
|
224
|
+
}
|
|
225
|
+
if (clauses.length > 0) sql += ` WHERE ${clauses.join(" AND ")}`;
|
|
226
|
+
}
|
|
227
|
+
const row = this.getDb().prepare(sql).get(...params);
|
|
228
|
+
return row?.cnt ?? 0;
|
|
229
|
+
}
|
|
230
|
+
async isEmpty({ namespace, collection }) {
|
|
231
|
+
const scoped = this.scopedName(namespace, collection);
|
|
232
|
+
this.requireDeclared(scoped);
|
|
233
|
+
return await this.tableCount(scoped) === 0;
|
|
234
|
+
}
|
|
235
|
+
async queryDeclared(table, decl, filter) {
|
|
236
|
+
const isKvShape = decl.columns.size === 1 && decl.columns.has("data");
|
|
237
|
+
const cols = [`"${decl.primaryKey}"`, ...[...decl.columns].map((c) => `"${c}"`)].join(", ");
|
|
238
|
+
let sql = `SELECT ${cols} FROM "${table}"`;
|
|
84
239
|
const params = [];
|
|
85
240
|
const whereClauses = [];
|
|
241
|
+
const isColumn = (f) => f === decl.primaryKey || decl.columns.has(f);
|
|
242
|
+
const fieldExpr = (f) => {
|
|
243
|
+
if (isColumn(f)) return `"${f}"`;
|
|
244
|
+
if (isKvShape) return `json_extract("data", '$.${f}')`;
|
|
245
|
+
return "";
|
|
246
|
+
};
|
|
86
247
|
if (filter?.where) {
|
|
87
248
|
for (const [field, value] of Object.entries(filter.where)) {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
whereClauses.push(`json_extract(data, '$.${field}') = ?`);
|
|
93
|
-
params.push(typeof value === "object" ? JSON.stringify(value) : value);
|
|
94
|
-
}
|
|
249
|
+
const expr = fieldExpr(field);
|
|
250
|
+
if (!expr) continue;
|
|
251
|
+
whereClauses.push(`${expr} = ?`);
|
|
252
|
+
params.push(this.serializeColumnValue(value));
|
|
95
253
|
}
|
|
96
254
|
}
|
|
97
255
|
if (filter?.whereIn) {
|
|
98
256
|
for (const [field, values] of Object.entries(filter.whereIn)) {
|
|
257
|
+
const expr = fieldExpr(field);
|
|
258
|
+
if (!expr) continue;
|
|
99
259
|
const placeholders = values.map(() => "?").join(", ");
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
} else {
|
|
103
|
-
whereClauses.push(`json_extract(data, '$.${field}') IN (${placeholders})`);
|
|
104
|
-
}
|
|
105
|
-
params.push(...values);
|
|
260
|
+
whereClauses.push(`${expr} IN (${placeholders})`);
|
|
261
|
+
for (const v of values) params.push(this.serializeColumnValue(v));
|
|
106
262
|
}
|
|
107
263
|
}
|
|
108
264
|
if (filter?.whereBetween) {
|
|
109
265
|
for (const [field, [low, high]] of Object.entries(filter.whereBetween)) {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
}
|
|
115
|
-
params.push(low, high);
|
|
266
|
+
const expr = fieldExpr(field);
|
|
267
|
+
if (!expr) continue;
|
|
268
|
+
whereClauses.push(`${expr} BETWEEN ? AND ?`);
|
|
269
|
+
params.push(this.serializeColumnValue(low), this.serializeColumnValue(high));
|
|
116
270
|
}
|
|
117
271
|
}
|
|
118
|
-
if (whereClauses.length > 0) {
|
|
119
|
-
sql += ` WHERE ${whereClauses.join(" AND ")}`;
|
|
120
|
-
}
|
|
272
|
+
if (whereClauses.length > 0) sql += ` WHERE ${whereClauses.join(" AND ")}`;
|
|
121
273
|
if (filter?.orderBy) {
|
|
122
|
-
const
|
|
123
|
-
if (
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
sql += ` ORDER BY json_extract(data, '$.${filter.orderBy.field}') ${dir}`;
|
|
274
|
+
const expr = fieldExpr(filter.orderBy.field);
|
|
275
|
+
if (expr) {
|
|
276
|
+
const dir = filter.orderBy.direction === "desc" ? "DESC" : "ASC";
|
|
277
|
+
sql += ` ORDER BY ${expr} ${dir}`;
|
|
127
278
|
}
|
|
128
279
|
}
|
|
129
|
-
if (filter?.limit) {
|
|
280
|
+
if (filter?.limit !== void 0) {
|
|
130
281
|
sql += ` LIMIT ?`;
|
|
131
282
|
params.push(filter.limit);
|
|
132
283
|
}
|
|
133
|
-
if (filter?.offset) {
|
|
284
|
+
if (filter?.offset !== void 0) {
|
|
134
285
|
sql += ` OFFSET ?`;
|
|
135
286
|
params.push(filter.offset);
|
|
136
287
|
}
|
|
137
288
|
const rows = this.getDb().prepare(sql).all(...params);
|
|
138
|
-
return rows.map((
|
|
139
|
-
id
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
this.ensureCollectionTable(collection);
|
|
167
|
-
const row = this.getDb().prepare(`SELECT COUNT(*) AS cnt FROM "${collection}"`).get();
|
|
168
|
-
return (row?.cnt ?? 0) === 0;
|
|
289
|
+
return rows.map((r) => {
|
|
290
|
+
const id = String(r[decl.primaryKey] ?? "");
|
|
291
|
+
if (isKvShape) {
|
|
292
|
+
const v = r["data"];
|
|
293
|
+
if (typeof v === "string" && (v.startsWith("{") || v.startsWith("["))) {
|
|
294
|
+
try {
|
|
295
|
+
return { id, data: JSON.parse(v) };
|
|
296
|
+
} catch {
|
|
297
|
+
return { id, data: { value: v } };
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
return { id, data: v == null ? {} : { value: v } };
|
|
301
|
+
}
|
|
302
|
+
const data = { [decl.primaryKey]: r[decl.primaryKey] ?? null };
|
|
303
|
+
for (const c of decl.columns) {
|
|
304
|
+
const v = r[c];
|
|
305
|
+
if (typeof v === "string" && (v.startsWith("{") || v.startsWith("["))) {
|
|
306
|
+
try {
|
|
307
|
+
data[c] = JSON.parse(v);
|
|
308
|
+
} catch {
|
|
309
|
+
data[c] = v;
|
|
310
|
+
}
|
|
311
|
+
} else {
|
|
312
|
+
data[c] = v ?? null;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
return { id, data };
|
|
316
|
+
});
|
|
169
317
|
}
|
|
170
318
|
// ---------------------------------------------------------------------------
|
|
171
319
|
// Legacy SettingsStore compatibility (used by ConfigManager.setSettingsStore)
|
|
172
320
|
// ---------------------------------------------------------------------------
|
|
173
321
|
/** Get a system setting by dot-notation key */
|
|
174
322
|
getSystem(key) {
|
|
175
|
-
this.
|
|
323
|
+
this.requireDeclared("system-settings");
|
|
176
324
|
const row = this.getDb().prepare('SELECT data FROM "system-settings" WHERE id = ?').get(key);
|
|
177
325
|
if (!row) return void 0;
|
|
178
326
|
return JSON.parse(row.data);
|
|
179
327
|
}
|
|
180
328
|
/** Set a system setting */
|
|
181
329
|
setSystem(key, value) {
|
|
182
|
-
this.
|
|
330
|
+
this.requireDeclared("system-settings");
|
|
183
331
|
this.getDb().prepare('INSERT INTO "system-settings" (id, data) VALUES (?, ?) ON CONFLICT(id) DO UPDATE SET data = excluded.data').run(key, JSON.stringify(value));
|
|
184
332
|
}
|
|
185
333
|
/** Get all system settings as flat key-value */
|
|
186
334
|
getAllSystem() {
|
|
187
|
-
this.
|
|
335
|
+
this.requireDeclared("system-settings");
|
|
188
336
|
const rows = this.getDb().prepare('SELECT id, data FROM "system-settings"').all();
|
|
189
337
|
return Object.fromEntries(rows.map((r) => [r.id, JSON.parse(r.data)]));
|
|
190
338
|
}
|
|
191
339
|
/** Get all settings for an addon */
|
|
192
340
|
getAllAddon(addonId) {
|
|
193
|
-
this.
|
|
341
|
+
this.requireDeclared("addon-settings");
|
|
194
342
|
const rows = this.getDb().prepare(`SELECT id, data FROM "addon-settings" WHERE json_extract(data, '$.addonId') = ?`).all(addonId);
|
|
195
343
|
if (rows.length === 0) return {};
|
|
196
344
|
const result = {};
|
|
197
345
|
for (const row of rows) {
|
|
198
|
-
const parsed =
|
|
346
|
+
const parsed = parseRowData(row.data);
|
|
199
347
|
const key = row.id.startsWith(`${addonId}.`) ? row.id.slice(addonId.length + 1) : row.id;
|
|
200
|
-
|
|
348
|
+
const isWrapper = parsed !== null && typeof parsed === "object" && "addonId" in parsed && "key" in parsed;
|
|
349
|
+
if (isWrapper) {
|
|
350
|
+
const wrapper = parsed;
|
|
351
|
+
if ("value" in wrapper) {
|
|
352
|
+
result[key] = wrapper.value;
|
|
353
|
+
}
|
|
354
|
+
} else {
|
|
355
|
+
result[key] = parsed;
|
|
356
|
+
}
|
|
201
357
|
}
|
|
202
358
|
return result;
|
|
203
359
|
}
|
|
204
360
|
/** Bulk-set all settings for an addon */
|
|
205
361
|
setAllAddon(addonId, config) {
|
|
206
|
-
this.
|
|
362
|
+
this.requireDeclared("addon-settings");
|
|
207
363
|
const db = this.getDb();
|
|
208
364
|
const deleteStmt = db.prepare(`DELETE FROM "addon-settings" WHERE id LIKE ? || '%'`);
|
|
209
365
|
const insertStmt = db.prepare('INSERT INTO "addon-settings" (id, data) VALUES (?, ?)');
|
|
210
366
|
db.transaction(() => {
|
|
211
367
|
deleteStmt.run(`${addonId}.`);
|
|
212
368
|
for (const [key, value] of Object.entries(config)) {
|
|
369
|
+
if (value === void 0) continue;
|
|
213
370
|
insertStmt.run(`${addonId}.${key}`, JSON.stringify({ addonId, key, value }));
|
|
214
371
|
}
|
|
215
372
|
})();
|
|
@@ -230,9 +387,41 @@ var SqliteSettingsBackend = class {
|
|
|
230
387
|
setDevice(deviceId, key, value) {
|
|
231
388
|
this.setScopedKey("device-settings", deviceId, key, value);
|
|
232
389
|
}
|
|
390
|
+
// ── Addon-device settings (per-device overrides of an addon's config) ──
|
|
391
|
+
//
|
|
392
|
+
// Storage key format: "<addonId>:<deviceId>.<field>" inside the
|
|
393
|
+
// "addon-device-settings" collection. Uses the same JSON-blob layout
|
|
394
|
+
// as the other scoped collections so no schema change is required.
|
|
395
|
+
getAddonDevice(addonId, deviceId) {
|
|
396
|
+
return this.getAllScoped("addon-device-settings", `${addonId}:${deviceId}`);
|
|
397
|
+
}
|
|
398
|
+
setAddonDevice(addonId, deviceId, values) {
|
|
399
|
+
this.requireDeclared("addon-device-settings");
|
|
400
|
+
const db = this.getDb();
|
|
401
|
+
const prefix = `${addonId}:${deviceId}.`;
|
|
402
|
+
const deleteStmt = db.prepare(`DELETE FROM "addon-device-settings" WHERE id LIKE ? || '%'`);
|
|
403
|
+
const insertStmt = db.prepare(
|
|
404
|
+
`INSERT INTO "addon-device-settings" (id, data) VALUES (?, ?)
|
|
405
|
+
ON CONFLICT(id) DO UPDATE SET data = excluded.data`
|
|
406
|
+
);
|
|
407
|
+
db.transaction(() => {
|
|
408
|
+
deleteStmt.run(prefix);
|
|
409
|
+
for (const [key, value] of Object.entries(values)) {
|
|
410
|
+
insertStmt.run(
|
|
411
|
+
`${prefix}${key}`,
|
|
412
|
+
JSON.stringify({ addonId, deviceId, key, value })
|
|
413
|
+
);
|
|
414
|
+
}
|
|
415
|
+
})();
|
|
416
|
+
}
|
|
417
|
+
clearAddonDevice(addonId, deviceId) {
|
|
418
|
+
this.requireDeclared("addon-device-settings");
|
|
419
|
+
const prefix = `${addonId}:${deviceId}.`;
|
|
420
|
+
this.getDb().prepare(`DELETE FROM "addon-device-settings" WHERE id LIKE ? || '%'`).run(prefix);
|
|
421
|
+
}
|
|
233
422
|
/** Seed system-settings with runtime defaults (first boot) */
|
|
234
423
|
async seedDefaults() {
|
|
235
|
-
this.
|
|
424
|
+
this.requireDeclared("system-settings");
|
|
236
425
|
const insert = this.getDb().prepare(
|
|
237
426
|
'INSERT OR IGNORE INTO "system-settings" (id, data) VALUES (?, ?)'
|
|
238
427
|
);
|
|
@@ -245,41 +434,73 @@ var SqliteSettingsBackend = class {
|
|
|
245
434
|
// ---------------------------------------------------------------------------
|
|
246
435
|
// Private helpers
|
|
247
436
|
// ---------------------------------------------------------------------------
|
|
437
|
+
/**
|
|
438
|
+
* Expose the raw better-sqlite3 Database instance for components that
|
|
439
|
+
* need direct SQL access (e.g. DeviceStore, ConfigStore).
|
|
440
|
+
* Returns null if the backend has not been initialized yet.
|
|
441
|
+
*/
|
|
442
|
+
getDatabase() {
|
|
443
|
+
return this.db;
|
|
444
|
+
}
|
|
248
445
|
getDb() {
|
|
249
446
|
if (!this.db) throw new Error("SqliteSettingsBackend not initialized \u2014 call initialize() first");
|
|
250
447
|
return this.db;
|
|
251
448
|
}
|
|
252
|
-
ensureCollectionTable(collection) {
|
|
253
|
-
if (this.ensuredTables.has(collection)) return;
|
|
254
|
-
this.getDb().exec(
|
|
255
|
-
`CREATE TABLE IF NOT EXISTS "${collection}" (id TEXT PRIMARY KEY, data TEXT NOT NULL)`
|
|
256
|
-
);
|
|
257
|
-
this.ensuredTables.add(collection);
|
|
258
|
-
}
|
|
259
449
|
getAllScoped(collection, scopeId) {
|
|
260
|
-
this.
|
|
450
|
+
this.requireDeclared(collection);
|
|
261
451
|
const rows = this.getDb().prepare(`SELECT id, data FROM "${collection}" WHERE id LIKE ? || '.%'`).all(scopeId);
|
|
262
452
|
const result = {};
|
|
263
453
|
for (const row of rows) {
|
|
264
454
|
const key = row.id.slice(scopeId.length + 1);
|
|
265
|
-
const parsed =
|
|
455
|
+
const parsed = parseRowData(row.data);
|
|
266
456
|
result[key] = parsed.value ?? parsed;
|
|
267
457
|
}
|
|
268
458
|
return result;
|
|
269
459
|
}
|
|
270
460
|
setScopedKey(collection, scopeId, key, value) {
|
|
271
|
-
this.
|
|
461
|
+
this.requireDeclared(collection);
|
|
272
462
|
this.getDb().prepare(`INSERT INTO "${collection}" (id, data) VALUES (?, ?) ON CONFLICT(id) DO UPDATE SET data = excluded.data`).run(`${scopeId}.${key}`, JSON.stringify({ scopeId, key, value }));
|
|
273
463
|
}
|
|
464
|
+
// ── Declared collections (typed SQL tables behind the generic cap) ──
|
|
465
|
+
scopedName(namespace, collection) {
|
|
466
|
+
return namespace ? `${namespace}:${collection}` : collection;
|
|
467
|
+
}
|
|
468
|
+
async declareCollection(input) {
|
|
469
|
+
const table = this.scopedName(input.namespace, input.collection);
|
|
470
|
+
if (this.declaredCollections.has(table)) return;
|
|
471
|
+
const hasId = input.columns.some((c) => c.primaryKey === true);
|
|
472
|
+
const columns = hasId ? input.columns : [{ name: "id", type: "TEXT", primaryKey: true, notNull: true }, ...input.columns];
|
|
473
|
+
const schema = {
|
|
474
|
+
columns: columns.map((c) => ({
|
|
475
|
+
name: c.name,
|
|
476
|
+
// SQLite treats JSON as TEXT; we serialize object values on write
|
|
477
|
+
// and parse on read.
|
|
478
|
+
type: c.type === "JSON" ? "TEXT" : c.type,
|
|
479
|
+
...c.primaryKey !== void 0 ? { primaryKey: c.primaryKey } : {},
|
|
480
|
+
...c.notNull !== void 0 ? { notNull: c.notNull } : {},
|
|
481
|
+
...c.unique !== void 0 ? { unique: c.unique } : {}
|
|
482
|
+
})),
|
|
483
|
+
...input.indexes ? { indexes: input.indexes.map((i) => ({
|
|
484
|
+
name: i.name,
|
|
485
|
+
columns: i.columns,
|
|
486
|
+
...i.unique !== void 0 ? { unique: i.unique } : {}
|
|
487
|
+
})) } : {}
|
|
488
|
+
};
|
|
489
|
+
await this.ensureTable(table, schema);
|
|
490
|
+
const pkCol = columns.find((c) => c.primaryKey === true);
|
|
491
|
+
const primaryKey = pkCol ? pkCol.name : "id";
|
|
492
|
+
const columnNames = new Set(columns.filter((c) => c.name !== primaryKey).map((c) => c.name));
|
|
493
|
+
this.declaredCollections.set(table, { primaryKey, columns: columnNames });
|
|
494
|
+
}
|
|
495
|
+
/** Serialise per-column values for SQL binding: objects → JSON, booleans → 0/1. */
|
|
496
|
+
serializeColumnValue(v) {
|
|
497
|
+
if (v === null || v === void 0) return v ?? null;
|
|
498
|
+
if (typeof v === "boolean") return v ? 1 : 0;
|
|
499
|
+
if (typeof v === "object") return JSON.stringify(v);
|
|
500
|
+
return v;
|
|
501
|
+
}
|
|
274
502
|
// ── Structured table operations ────────────────────────────────────
|
|
275
503
|
async ensureTable(table, schema) {
|
|
276
|
-
if (!schema) {
|
|
277
|
-
if (!this.ensuredTables.has(table)) {
|
|
278
|
-
this.getDb().exec(`CREATE TABLE IF NOT EXISTS "${table}" (id TEXT PRIMARY KEY, data TEXT NOT NULL)`);
|
|
279
|
-
this.ensuredTables.add(table);
|
|
280
|
-
}
|
|
281
|
-
return;
|
|
282
|
-
}
|
|
283
504
|
if (this.structuredTables.has(table)) return;
|
|
284
505
|
const colDefs = schema.columns.map((col) => {
|
|
285
506
|
const parts = [`"${col.name}" ${col.type}`];
|
|
@@ -291,6 +512,19 @@ var SqliteSettingsBackend = class {
|
|
|
291
512
|
}
|
|
292
513
|
return parts.join(" ");
|
|
293
514
|
});
|
|
515
|
+
const existingCols = this.getDb().prepare(`PRAGMA table_info("${table}")`).all();
|
|
516
|
+
if (existingCols.length > 0) {
|
|
517
|
+
const existingNames = new Set(existingCols.map((c) => c.name));
|
|
518
|
+
const declaredNames = new Set(schema.columns.map((c) => c.name));
|
|
519
|
+
const missingDeclared = schema.columns.some((c) => !existingNames.has(c.name));
|
|
520
|
+
const isLegacyKv = existingCols.length <= 2 && existingNames.has("data") && !declaredNames.has("data");
|
|
521
|
+
if (isLegacyKv || missingDeclared) {
|
|
522
|
+
try {
|
|
523
|
+
this.getDb().exec(`DROP TABLE "${table}"`);
|
|
524
|
+
} catch {
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
}
|
|
294
528
|
this.getDb().exec(`CREATE TABLE IF NOT EXISTS "${table}" (${colDefs.join(", ")})`);
|
|
295
529
|
if (schema.indexes) {
|
|
296
530
|
for (const idx of schema.indexes) {
|
|
@@ -346,12 +580,13 @@ var SqliteSettingsBackend = class {
|
|
|
346
580
|
sql += ` OFFSET ?`;
|
|
347
581
|
values.push(options.offset);
|
|
348
582
|
}
|
|
349
|
-
|
|
583
|
+
const rows = this.getDb().prepare(sql).all(...values);
|
|
584
|
+
return rows.flatMap((r) => (0, import_types.asJsonObject)(r) ? [(0, import_types.asJsonObject)(r)] : []);
|
|
350
585
|
}
|
|
351
586
|
async tableGet(table, filter) {
|
|
352
587
|
const { whereSql, whereValues } = this.buildWhere(filter);
|
|
353
588
|
const row = this.getDb().prepare(`SELECT * FROM "${table}"${whereSql} LIMIT 1`).get(...whereValues);
|
|
354
|
-
return row
|
|
589
|
+
return (0, import_types.asJsonObject)(row);
|
|
355
590
|
}
|
|
356
591
|
async tableCount(table, filter) {
|
|
357
592
|
let sql = `SELECT COUNT(*) as count FROM "${table}"`;
|
|
@@ -361,8 +596,8 @@ var SqliteSettingsBackend = class {
|
|
|
361
596
|
sql += whereSql;
|
|
362
597
|
values.push(...whereValues);
|
|
363
598
|
}
|
|
364
|
-
const row = this.getDb().prepare(sql).get(...values);
|
|
365
|
-
return row.count;
|
|
599
|
+
const row = (0, import_types.asJsonObject)(this.getDb().prepare(sql).get(...values));
|
|
600
|
+
return typeof row?.count === "number" ? row.count : 0;
|
|
366
601
|
}
|
|
367
602
|
buildWhere(filter) {
|
|
368
603
|
const conditions = [];
|
|
@@ -377,41 +612,44 @@ var SqliteSettingsBackend = class {
|
|
|
377
612
|
};
|
|
378
613
|
|
|
379
614
|
// src/builtins/sqlite-storage/sqlite-settings.addon.ts
|
|
380
|
-
var SqliteSettingsAddon = class {
|
|
381
|
-
manifest = {
|
|
382
|
-
id: "sqlite-settings",
|
|
383
|
-
name: "SQLite Settings",
|
|
384
|
-
version: "1.0.0",
|
|
385
|
-
capabilities: [{ name: "settings-store", mode: "singleton" }]
|
|
386
|
-
};
|
|
615
|
+
var SqliteSettingsAddon = class extends import_types2.BaseAddon {
|
|
387
616
|
backend = null;
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
617
|
+
constructor() {
|
|
618
|
+
super({});
|
|
619
|
+
}
|
|
620
|
+
async onInitialize() {
|
|
621
|
+
const addonId = this.ctx.id.replace("addon:", "");
|
|
622
|
+
let pathSource = "fallback (hardcoded)";
|
|
623
|
+
const dbPath = await this.ctx.api.storage.resolve.query({ location: "addons-data", relativePath: `${addonId}/camstack.db` }).then((p) => {
|
|
624
|
+
pathSource = "storage capability (addons-data)";
|
|
625
|
+
return p;
|
|
626
|
+
}).catch(() => {
|
|
627
|
+
if (this.ctx.dataDir) {
|
|
628
|
+
pathSource = "dataDir";
|
|
629
|
+
return `${this.ctx.dataDir}/camstack.db`;
|
|
630
|
+
}
|
|
631
|
+
return "camstack-data/addons-data/sqlite-settings/camstack.db";
|
|
632
|
+
});
|
|
633
|
+
let dbExists = false;
|
|
634
|
+
try {
|
|
635
|
+
const fs = await import("fs");
|
|
636
|
+
dbExists = fs.existsSync(dbPath);
|
|
637
|
+
} catch (err) {
|
|
638
|
+
this.ctx.logger.warn("Failed to check DB file existence", { meta: { error: (0, import_types2.errMsg)(err) } });
|
|
639
|
+
}
|
|
640
|
+
this.ctx.logger.info("DB path resolved", { meta: { pathSource, dbPath } });
|
|
641
|
+
this.ctx.logger.info("DB file status", { meta: { dbExists } });
|
|
642
|
+
this.backend = new SqliteSettingsBackend(dbPath, { ...import_types2.RUNTIME_DEFAULTS });
|
|
392
643
|
await this.backend.initialize();
|
|
393
|
-
|
|
644
|
+
this.ctx.logger.info("Initialized successfully");
|
|
645
|
+
return [{ capability: import_types2.settingsStoreCapability, provider: this.backend }];
|
|
394
646
|
}
|
|
395
|
-
async
|
|
647
|
+
async onShutdown() {
|
|
396
648
|
await this.backend?.shutdown();
|
|
397
649
|
}
|
|
398
650
|
getBackend() {
|
|
399
651
|
return this.backend;
|
|
400
652
|
}
|
|
401
|
-
getCapabilityProvider(name) {
|
|
402
|
-
if (name === "settings-store" && this.backend) {
|
|
403
|
-
return this.backend;
|
|
404
|
-
}
|
|
405
|
-
return null;
|
|
406
|
-
}
|
|
407
|
-
getConfigSchema() {
|
|
408
|
-
return { sections: [] };
|
|
409
|
-
}
|
|
410
|
-
getConfig() {
|
|
411
|
-
return {};
|
|
412
|
-
}
|
|
413
|
-
async onConfigChange(_config) {
|
|
414
|
-
}
|
|
415
653
|
};
|
|
416
654
|
var sqlite_settings_addon_default = SqliteSettingsAddon;
|
|
417
655
|
// Annotate the CommonJS export names for ESM import in node:
|