@digitalpaws/starr 0.0.1
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/README.rst +75 -0
- package/data/.nfs.811bbb0d.43c34.4 +0 -0
- package/data/.nfs.81ba4cd0.ffff87804.4 +0 -0
- package/data/.nfs.82d127f7.fffff5704.4 +0 -0
- package/data/.nfs.82d127fe.fffff5704.4 +0 -0
- package/data/.nfs.82d159a5.fffff5aa4.4 +0 -0
- package/data/.nfs.82d159aa.fffff5aa4.4 +0 -0
- package/data/.nfs.82d15a9c.fffff5bb4.4 +0 -0
- package/data/.nfs.82d15aa1.fffff5bb4.4 +0 -0
- package/data/.nfs.82d15c65.fffff5c24.4 +0 -0
- package/data/.nfs.82d1852c.fffff5e24.4 +0 -0
- package/data/.nfs.82d18536.fffff5e24.4 +0 -0
- package/data/.nfs.82d18dbe.fffff5e44.4 +0 -0
- package/data/.nfs.82d18dc6.fffff5e44.4 +0 -0
- package/data/.nfs.8309a3f5.fffff82f4.4 +0 -0
- package/data/.nfs.8309a3fe.fffff82f4.4 +0 -0
- package/data/.nfs.8314122e.fffffb044.4 +0 -0
- package/data/.nfs.83141236.fffffb044.4 +0 -0
- package/data/.nfs.833c4fe9.fffffbc34.4 +0 -0
- package/data/.nfs.833c4ff3.fffffbc34.4 +0 -0
- package/data/logs/starr.log +66244 -0
- package/data/starr.db +0 -0
- package/data/starr.db-shm +0 -0
- package/data/starr.db-wal +0 -0
- package/data/starr.jwt +1 -0
- package/data/starr.key +1 -0
- package/dist/auth/auth-service.d.ts +42 -0
- package/dist/auth/auth-service.d.ts.map +1 -0
- package/dist/auth/auth-service.js +226 -0
- package/dist/auth/auth-service.js.map +1 -0
- package/dist/auth/http-server.d.ts +16 -0
- package/dist/auth/http-server.d.ts.map +1 -0
- package/dist/auth/http-server.js +207 -0
- package/dist/auth/http-server.js.map +1 -0
- package/dist/auth/jwt-secret.d.ts +6 -0
- package/dist/auth/jwt-secret.d.ts.map +1 -0
- package/dist/auth/jwt-secret.js +51 -0
- package/dist/auth/jwt-secret.js.map +1 -0
- package/dist/auth/oidc/oidc-flow.d.ts +60 -0
- package/dist/auth/oidc/oidc-flow.d.ts.map +1 -0
- package/dist/auth/oidc/oidc-flow.js +201 -0
- package/dist/auth/oidc/oidc-flow.js.map +1 -0
- package/dist/auth/oidc/oidc-provider-service.d.ts +59 -0
- package/dist/auth/oidc/oidc-provider-service.d.ts.map +1 -0
- package/dist/auth/oidc/oidc-provider-service.js +124 -0
- package/dist/auth/oidc/oidc-provider-service.js.map +1 -0
- package/dist/auth/oidc/user-service.d.ts +67 -0
- package/dist/auth/oidc/user-service.d.ts.map +1 -0
- package/dist/auth/oidc/user-service.js +177 -0
- package/dist/auth/oidc/user-service.js.map +1 -0
- package/dist/auth/rate-limiter.d.ts +31 -0
- package/dist/auth/rate-limiter.d.ts.map +1 -0
- package/dist/auth/rate-limiter.js +70 -0
- package/dist/auth/rate-limiter.js.map +1 -0
- package/dist/config/config-service.d.ts +83 -0
- package/dist/config/config-service.d.ts.map +1 -0
- package/dist/config/config-service.js +309 -0
- package/dist/config/config-service.js.map +1 -0
- package/dist/config/validate.d.ts +12 -0
- package/dist/config/validate.d.ts.map +1 -0
- package/dist/config/validate.js +140 -0
- package/dist/config/validate.js.map +1 -0
- package/dist/content-types/registry.d.ts +75 -0
- package/dist/content-types/registry.d.ts.map +1 -0
- package/dist/content-types/registry.js +171 -0
- package/dist/content-types/registry.js.map +1 -0
- package/dist/datastore/db.d.ts +9 -0
- package/dist/datastore/db.d.ts.map +1 -0
- package/dist/datastore/db.js +27 -0
- package/dist/datastore/db.js.map +1 -0
- package/dist/datastore/encrypt.d.ts +9 -0
- package/dist/datastore/encrypt.d.ts.map +1 -0
- package/dist/datastore/encrypt.js +84 -0
- package/dist/datastore/encrypt.js.map +1 -0
- package/dist/datastore/migrations.d.ts +10 -0
- package/dist/datastore/migrations.d.ts.map +1 -0
- package/dist/datastore/migrations.js +340 -0
- package/dist/datastore/migrations.js.map +1 -0
- package/dist/datastore/storage.d.ts +4 -0
- package/dist/datastore/storage.d.ts.map +1 -0
- package/dist/datastore/storage.js +60 -0
- package/dist/datastore/storage.js.map +1 -0
- package/dist/events/event-bus.d.ts +15 -0
- package/dist/events/event-bus.d.ts.map +1 -0
- package/dist/events/event-bus.js +140 -0
- package/dist/events/event-bus.js.map +1 -0
- package/dist/events/spine.d.ts +31 -0
- package/dist/events/spine.d.ts.map +1 -0
- package/dist/events/spine.js +68 -0
- package/dist/events/spine.js.map +1 -0
- package/dist/events/trace.d.ts +24 -0
- package/dist/events/trace.d.ts.map +1 -0
- package/dist/events/trace.js +33 -0
- package/dist/events/trace.js.map +1 -0
- package/dist/host.d.ts +65 -0
- package/dist/host.d.ts.map +1 -0
- package/dist/host.js +94 -0
- package/dist/host.js.map +1 -0
- package/dist/library/importer.d.ts +29 -0
- package/dist/library/importer.d.ts.map +1 -0
- package/dist/library/importer.js +145 -0
- package/dist/library/importer.js.map +1 -0
- package/dist/library/library-cache.d.ts +46 -0
- package/dist/library/library-cache.d.ts.map +1 -0
- package/dist/library/library-cache.js +152 -0
- package/dist/library/library-cache.js.map +1 -0
- package/dist/library/library-config.d.ts +12 -0
- package/dist/library/library-config.d.ts.map +1 -0
- package/dist/library/library-config.js +10 -0
- package/dist/library/library-config.js.map +1 -0
- package/dist/library/library-scanner.d.ts +62 -0
- package/dist/library/library-scanner.d.ts.map +1 -0
- package/dist/library/library-scanner.js +336 -0
- package/dist/library/library-scanner.js.map +1 -0
- package/dist/library/library-service.d.ts +65 -0
- package/dist/library/library-service.d.ts.map +1 -0
- package/dist/library/library-service.js +197 -0
- package/dist/library/library-service.js.map +1 -0
- package/dist/library/metadata-matcher.d.ts +56 -0
- package/dist/library/metadata-matcher.d.ts.map +1 -0
- package/dist/library/metadata-matcher.js +207 -0
- package/dist/library/metadata-matcher.js.map +1 -0
- package/dist/library/naming.d.ts +8 -0
- package/dist/library/naming.d.ts.map +1 -0
- package/dist/library/naming.js +47 -0
- package/dist/library/naming.js.map +1 -0
- package/dist/library/scan-controller.d.ts +95 -0
- package/dist/library/scan-controller.d.ts.map +1 -0
- package/dist/library/scan-controller.js +379 -0
- package/dist/library/scan-controller.js.map +1 -0
- package/dist/library/source-resolver.d.ts +17 -0
- package/dist/library/source-resolver.d.ts.map +1 -0
- package/dist/library/source-resolver.js +75 -0
- package/dist/library/source-resolver.js.map +1 -0
- package/dist/logging/logger.d.ts +3 -0
- package/dist/logging/logger.d.ts.map +1 -0
- package/dist/logging/logger.js +107 -0
- package/dist/logging/logger.js.map +1 -0
- package/dist/main.d.ts +36 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +229 -0
- package/dist/main.js.map +1 -0
- package/dist/plugins/context.d.ts +10 -0
- package/dist/plugins/context.d.ts.map +1 -0
- package/dist/plugins/context.js +110 -0
- package/dist/plugins/context.js.map +1 -0
- package/dist/plugins/discovery.d.ts +2 -0
- package/dist/plugins/discovery.d.ts.map +1 -0
- package/dist/plugins/discovery.js +32 -0
- package/dist/plugins/discovery.js.map +1 -0
- package/dist/plugins/loader.d.ts +9 -0
- package/dist/plugins/loader.d.ts.map +1 -0
- package/dist/plugins/loader.js +245 -0
- package/dist/plugins/loader.js.map +1 -0
- package/dist/state/request-intake.d.ts +26 -0
- package/dist/state/request-intake.d.ts.map +1 -0
- package/dist/state/request-intake.js +74 -0
- package/dist/state/request-intake.js.map +1 -0
- package/dist/state/state-store.d.ts +146 -0
- package/dist/state/state-store.d.ts.map +1 -0
- package/dist/state/state-store.js +465 -0
- package/dist/state/state-store.js.map +1 -0
- package/dist/state/trace-store.d.ts +12 -0
- package/dist/state/trace-store.d.ts.map +1 -0
- package/dist/state/trace-store.js +24 -0
- package/dist/state/trace-store.js.map +1 -0
- package/dist/types/plugin.d.ts +93 -0
- package/dist/types/plugin.d.ts.map +1 -0
- package/dist/types/plugin.js +4 -0
- package/dist/types/plugin.js.map +1 -0
- package/dist/web/index.d.ts +10 -0
- package/dist/web/index.d.ts.map +1 -0
- package/dist/web/index.js +153 -0
- package/dist/web/index.js.map +1 -0
- package/dist/web/routes/activity.d.ts +6 -0
- package/dist/web/routes/activity.d.ts.map +1 -0
- package/dist/web/routes/activity.js +31 -0
- package/dist/web/routes/activity.js.map +1 -0
- package/dist/web/routes/auth.d.ts +6 -0
- package/dist/web/routes/auth.d.ts.map +1 -0
- package/dist/web/routes/auth.js +142 -0
- package/dist/web/routes/auth.js.map +1 -0
- package/dist/web/routes/config.d.ts +7 -0
- package/dist/web/routes/config.d.ts.map +1 -0
- package/dist/web/routes/config.js +204 -0
- package/dist/web/routes/config.js.map +1 -0
- package/dist/web/routes/content-types.d.ts +6 -0
- package/dist/web/routes/content-types.d.ts.map +1 -0
- package/dist/web/routes/content-types.js +29 -0
- package/dist/web/routes/content-types.js.map +1 -0
- package/dist/web/routes/discovery.d.ts +6 -0
- package/dist/web/routes/discovery.d.ts.map +1 -0
- package/dist/web/routes/discovery.js +111 -0
- package/dist/web/routes/discovery.js.map +1 -0
- package/dist/web/routes/library-import.d.ts +6 -0
- package/dist/web/routes/library-import.d.ts.map +1 -0
- package/dist/web/routes/library-import.js +120 -0
- package/dist/web/routes/library-import.js.map +1 -0
- package/dist/web/routes/library-management.d.ts +6 -0
- package/dist/web/routes/library-management.d.ts.map +1 -0
- package/dist/web/routes/library-management.js +120 -0
- package/dist/web/routes/library-management.js.map +1 -0
- package/dist/web/routes/library.d.ts +6 -0
- package/dist/web/routes/library.d.ts.map +1 -0
- package/dist/web/routes/library.js +28 -0
- package/dist/web/routes/library.js.map +1 -0
- package/dist/web/routes/oidc.d.ts +9 -0
- package/dist/web/routes/oidc.d.ts.map +1 -0
- package/dist/web/routes/oidc.js +203 -0
- package/dist/web/routes/oidc.js.map +1 -0
- package/dist/web/routes/requests.d.ts +6 -0
- package/dist/web/routes/requests.d.ts.map +1 -0
- package/dist/web/routes/requests.js +183 -0
- package/dist/web/routes/requests.js.map +1 -0
- package/dist/web/routes/sse.d.ts +6 -0
- package/dist/web/routes/sse.d.ts.map +1 -0
- package/dist/web/routes/sse.js +54 -0
- package/dist/web/routes/sse.js.map +1 -0
- package/package.json +33 -0
- package/package.json.license +2 -0
- package/src/auth/auth-service.ts +333 -0
- package/src/auth/http-server.ts +249 -0
- package/src/auth/jwt-secret.ts +65 -0
- package/src/auth/oidc/oidc-flow.ts +266 -0
- package/src/auth/oidc/oidc-provider-service.ts +174 -0
- package/src/auth/oidc/user-service.ts +227 -0
- package/src/auth/rate-limiter.ts +85 -0
- package/src/config/config-service.ts +434 -0
- package/src/config/validate.ts +153 -0
- package/src/content-types/registry.ts +194 -0
- package/src/datastore/db.ts +38 -0
- package/src/datastore/encrypt.ts +107 -0
- package/src/datastore/migrations.ts +367 -0
- package/src/datastore/storage.ts +83 -0
- package/src/events/event-bus.ts +180 -0
- package/src/events/spine.ts +87 -0
- package/src/events/trace.ts +38 -0
- package/src/host.ts +130 -0
- package/src/library/importer.ts +174 -0
- package/src/library/library-cache.ts +212 -0
- package/src/library/library-config.ts +22 -0
- package/src/library/library-scanner.ts +404 -0
- package/src/library/library-service.ts +282 -0
- package/src/library/metadata-matcher.ts +285 -0
- package/src/library/naming.ts +59 -0
- package/src/library/scan-controller.ts +490 -0
- package/src/library/source-resolver.ts +92 -0
- package/src/logging/logger.ts +115 -0
- package/src/main.ts +364 -0
- package/src/plugins/context.ts +138 -0
- package/src/plugins/discovery.ts +29 -0
- package/src/plugins/loader.ts +309 -0
- package/src/state/request-intake.ts +101 -0
- package/src/state/state-store.ts +621 -0
- package/src/state/trace-store.ts +27 -0
- package/src/types/plugin.ts +110 -0
- package/src/web/index.ts +182 -0
- package/src/web/routes/activity.ts +41 -0
- package/src/web/routes/auth.ts +170 -0
- package/src/web/routes/config.ts +282 -0
- package/src/web/routes/content-types.ts +42 -0
- package/src/web/routes/discovery.ts +156 -0
- package/src/web/routes/library-import.ts +196 -0
- package/src/web/routes/library-management.ts +140 -0
- package/src/web/routes/library.ts +46 -0
- package/src/web/routes/oidc.ts +260 -0
- package/src/web/routes/requests.ts +250 -0
- package/src/web/routes/sse.ts +92 -0
- package/tests/auth.test.ts +510 -0
- package/tests/config-route.test.ts +345 -0
- package/tests/config.test.ts +612 -0
- package/tests/content-types.test.ts +376 -0
- package/tests/datastore.test.ts +345 -0
- package/tests/e2e.test.ts +430 -0
- package/tests/event-bus.test.ts +925 -0
- package/tests/host.test.ts +449 -0
- package/tests/library-cache.test.ts +171 -0
- package/tests/library-scanner.test.ts +177 -0
- package/tests/library.test.ts +210 -0
- package/tests/loader-context.test.ts +644 -0
- package/tests/logger-jsonl.test.ts +154 -0
- package/tests/metadata-matcher.test.ts +198 -0
- package/tests/oidc.test.ts +218 -0
- package/tests/scan-controller.test.ts +328 -0
- package/tests/source-resolver.test.ts +100 -0
- package/tests/starr-video-integration.test.ts +153 -0
- package/tests/state-store.test.ts +700 -0
- package/tests/trace-history.test.ts +356 -0
- package/tsconfig.json +8 -0
- package/tsconfig.json.license +2 -0
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
// SPDX-License-Identifier: 0BSD
|
|
2
|
+
// SPDX-FileCopyrightText: 2025 Digi <digitalpaws@proton.me>
|
|
3
|
+
import { validateConfig } from "./validate.js";
|
|
4
|
+
import { encrypt, decrypt } from "../datastore/encrypt.js";
|
|
5
|
+
/**
|
|
6
|
+
* Sentinel value used to mask secret fields in UI-exported data.
|
|
7
|
+
* When update() receives this value for a secret field, it preserves
|
|
8
|
+
* the existing encrypted value rather than overwriting it.
|
|
9
|
+
*/
|
|
10
|
+
export const SECRET_UNCHANGED_SENTINEL = "__STARR_SECRET_UNCHANGED__";
|
|
11
|
+
export class ConfigService {
|
|
12
|
+
db;
|
|
13
|
+
logger;
|
|
14
|
+
onReload;
|
|
15
|
+
// Prepared statements
|
|
16
|
+
getStmt;
|
|
17
|
+
getByInstanceStmt;
|
|
18
|
+
upsertStmt;
|
|
19
|
+
listStmt;
|
|
20
|
+
listInstancesStmt;
|
|
21
|
+
deleteInstanceStmt;
|
|
22
|
+
constructor(db, logger, onReload) {
|
|
23
|
+
this.db = db;
|
|
24
|
+
this.logger = logger;
|
|
25
|
+
this.onReload = onReload;
|
|
26
|
+
this.getStmt = db.prepare('SELECT schema, "values", updated_at FROM plugin_config WHERE plugin_id = ?');
|
|
27
|
+
this.getByInstanceStmt = db.prepare('SELECT schema, "values", updated_at FROM plugin_config WHERE plugin_id = ? AND instance_id = ?');
|
|
28
|
+
this.upsertStmt = db.prepare(`
|
|
29
|
+
INSERT INTO plugin_config (plugin_id, instance_id, schema, "values", updated_at)
|
|
30
|
+
VALUES (?, ?, ?, ?, datetime('now'))
|
|
31
|
+
ON CONFLICT(plugin_id, instance_id) DO UPDATE SET
|
|
32
|
+
schema = excluded.schema,
|
|
33
|
+
"values" = excluded."values",
|
|
34
|
+
updated_at = datetime('now')
|
|
35
|
+
`);
|
|
36
|
+
this.listStmt = db.prepare('SELECT plugin_id, instance_id, schema, "values", updated_at FROM plugin_config ORDER BY plugin_id, instance_id');
|
|
37
|
+
this.listInstancesStmt = db.prepare('SELECT plugin_id, instance_id, schema, "values", updated_at FROM plugin_config WHERE plugin_id = ? ORDER BY instance_id');
|
|
38
|
+
this.deleteInstanceStmt = db.prepare("DELETE FROM plugin_config WHERE plugin_id = ? AND instance_id = ?");
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Called by the plugin context during register.
|
|
42
|
+
* Returns read-only validated values.
|
|
43
|
+
*/
|
|
44
|
+
register(pluginId, schema, instanceId = "default") {
|
|
45
|
+
const row = this.getByInstanceStmt.get(pluginId, instanceId);
|
|
46
|
+
if (row) {
|
|
47
|
+
// Existing row — decrypt secrets, re-validate against current schema
|
|
48
|
+
const storedValues = this.decryptValues(schema, JSON.parse(row.values));
|
|
49
|
+
// Merge stored values with defaults from the current schema so that
|
|
50
|
+
// newly added fields (e.g. libraryId) are picked up.
|
|
51
|
+
const mergedValues = { ...this.buildDefaults(schema), ...storedValues };
|
|
52
|
+
const result = validateConfig(schema, mergedValues);
|
|
53
|
+
if (result.valid) {
|
|
54
|
+
// Schema or values may have changed (new optional fields, new defaults).
|
|
55
|
+
// Persist the current schema so get/list reflect it, and persist the
|
|
56
|
+
// merged values so existing config is preserved.
|
|
57
|
+
const toPersist = this.encryptValues(schema, mergedValues);
|
|
58
|
+
this.upsertStmt.run(pluginId, instanceId, JSON.stringify(schema), JSON.stringify(toPersist));
|
|
59
|
+
return Object.freeze(result.values);
|
|
60
|
+
}
|
|
61
|
+
// Validation failed against current schema (e.g., new required field added)
|
|
62
|
+
this.logger.warn("config re-validation failed, marking needs-configuration", {
|
|
63
|
+
pluginId,
|
|
64
|
+
instanceId,
|
|
65
|
+
errors: result.errors,
|
|
66
|
+
});
|
|
67
|
+
// Persist the new schema (so get/list reflect current schema) but keep
|
|
68
|
+
// existing values so user's prior changes aren't lost.
|
|
69
|
+
const toPersistOnFail = this.encryptValues(schema, mergedValues);
|
|
70
|
+
this.upsertStmt.run(pluginId, instanceId, JSON.stringify(schema), JSON.stringify(toPersistOnFail));
|
|
71
|
+
// Return defaults, plugin will need configuration
|
|
72
|
+
const defaults = this.buildDefaults(schema);
|
|
73
|
+
return Object.freeze(defaults);
|
|
74
|
+
}
|
|
75
|
+
// No existing row — first registration
|
|
76
|
+
const defaults = this.buildDefaults(schema);
|
|
77
|
+
const result = validateConfig(schema, defaults);
|
|
78
|
+
if (!result.valid) {
|
|
79
|
+
// Has required fields without defaults — persist defaults and mark needs-configuration
|
|
80
|
+
this.logger.warn("config has missing required fields, needs-configuration", {
|
|
81
|
+
pluginId,
|
|
82
|
+
instanceId,
|
|
83
|
+
errors: result.errors,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
// Persist schema and values (encrypt secrets before storing)
|
|
87
|
+
const toPersist = this.encryptValues(schema, result.valid ? result.values : defaults);
|
|
88
|
+
this.upsertStmt.run(pluginId, instanceId, JSON.stringify(schema), JSON.stringify(toPersist));
|
|
89
|
+
return Object.freeze((result.valid ? result.values : defaults));
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Called by the admin settings API when saving config.
|
|
93
|
+
* Updates values, persists, and triggers reload.
|
|
94
|
+
*/
|
|
95
|
+
update(pluginId, schema, input, instanceId = "default") {
|
|
96
|
+
const result = validateConfig(schema, input);
|
|
97
|
+
if (!result.valid) {
|
|
98
|
+
// Validation errors block save (CF3)
|
|
99
|
+
return result;
|
|
100
|
+
}
|
|
101
|
+
// Before persisting, handle secret fields:
|
|
102
|
+
// If a secret field's value is the sentinel, retrieve and use the existing
|
|
103
|
+
// decrypted value (so encryptValues() can re-encrypt it properly).
|
|
104
|
+
const existingRow = this.getByInstanceStmt.get(pluginId, instanceId);
|
|
105
|
+
if (existingRow) {
|
|
106
|
+
const existingParsed = JSON.parse(existingRow.values);
|
|
107
|
+
const existingDecrypted = this.decryptValues(schema, existingParsed);
|
|
108
|
+
for (const [key, field] of Object.entries(schema)) {
|
|
109
|
+
const f = field;
|
|
110
|
+
if (f.type === "secret") {
|
|
111
|
+
if (result.values[key] === SECRET_UNCHANGED_SENTINEL) {
|
|
112
|
+
// Use the decrypted existing value — will be re-encrypted below
|
|
113
|
+
result.values[key] = existingDecrypted[key];
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// Encrypt secret fields before persisting
|
|
119
|
+
const toPersist = this.encryptValues(schema, result.values);
|
|
120
|
+
// Persist
|
|
121
|
+
this.upsertStmt.run(pluginId, instanceId, JSON.stringify(schema), JSON.stringify(toPersist));
|
|
122
|
+
// Trigger reload
|
|
123
|
+
this.logger.info("config updated, triggering reload", { pluginId, instanceId });
|
|
124
|
+
this.onReload?.(pluginId);
|
|
125
|
+
return result;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Called by the admin UI to get render data.
|
|
129
|
+
* Returns schema + values (with secrets masked) + needsConfiguration flag.
|
|
130
|
+
*/
|
|
131
|
+
get(pluginId, instanceId = "default") {
|
|
132
|
+
const row = this.getByInstanceStmt.get(pluginId, instanceId);
|
|
133
|
+
if (!row)
|
|
134
|
+
return undefined;
|
|
135
|
+
const schema = JSON.parse(row.schema);
|
|
136
|
+
const storedValues = JSON.parse(row.values);
|
|
137
|
+
// Decrypt secrets for internal computation
|
|
138
|
+
const decryptedValues = this.decryptValues(schema, storedValues);
|
|
139
|
+
// Mask secrets for UI output
|
|
140
|
+
const maskedValues = this.maskSecretValues(schema, decryptedValues);
|
|
141
|
+
const needsConfiguration = this.computeNeedsConfiguration(schema, decryptedValues);
|
|
142
|
+
return {
|
|
143
|
+
schema,
|
|
144
|
+
values: maskedValues,
|
|
145
|
+
needsConfiguration,
|
|
146
|
+
updatedAt: row.updated_at,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Called by the admin UI to list all plugins with config status.
|
|
151
|
+
*/
|
|
152
|
+
list() {
|
|
153
|
+
const rows = this.listStmt.all();
|
|
154
|
+
return rows
|
|
155
|
+
.filter((row) => !row.plugin_id.startsWith("_core."))
|
|
156
|
+
.map((row) => {
|
|
157
|
+
const schema = JSON.parse(row.schema);
|
|
158
|
+
const values = JSON.parse(row.values);
|
|
159
|
+
const decryptedValues = this.decryptValues(schema, values);
|
|
160
|
+
const needsConfiguration = this.computeNeedsConfiguration(schema, decryptedValues);
|
|
161
|
+
return {
|
|
162
|
+
pluginId: row.plugin_id,
|
|
163
|
+
instanceId: row.instance_id,
|
|
164
|
+
needsConfiguration,
|
|
165
|
+
updatedAt: row.updated_at,
|
|
166
|
+
libraryId: decryptedValues.libraryId,
|
|
167
|
+
};
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* List all instances for a given plugin.
|
|
172
|
+
*/
|
|
173
|
+
listInstances(pluginId) {
|
|
174
|
+
const rows = this.listInstancesStmt.all(pluginId);
|
|
175
|
+
return rows
|
|
176
|
+
.filter((row) => !row.plugin_id.startsWith("_core."))
|
|
177
|
+
.map((row) => {
|
|
178
|
+
const schema = JSON.parse(row.schema);
|
|
179
|
+
const values = JSON.parse(row.values);
|
|
180
|
+
const decryptedValues = this.decryptValues(schema, values);
|
|
181
|
+
const needsConfiguration = this.computeNeedsConfiguration(schema, decryptedValues);
|
|
182
|
+
return {
|
|
183
|
+
pluginId: row.plugin_id,
|
|
184
|
+
instanceId: row.instance_id,
|
|
185
|
+
needsConfiguration,
|
|
186
|
+
updatedAt: row.updated_at,
|
|
187
|
+
libraryId: decryptedValues.libraryId,
|
|
188
|
+
};
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Create a new plugin instance with the given instanceId and config values.
|
|
193
|
+
* Validates and persists.
|
|
194
|
+
*/
|
|
195
|
+
createInstance(pluginId, instanceId, schema, values) {
|
|
196
|
+
// Validate
|
|
197
|
+
const result = validateConfig(schema, values);
|
|
198
|
+
if (!result.valid)
|
|
199
|
+
return result;
|
|
200
|
+
// Check for duplicate instance
|
|
201
|
+
const existing = this.get(pluginId, instanceId);
|
|
202
|
+
if (existing) {
|
|
203
|
+
return { valid: false, values: {}, errors: [{ field: "instanceId", message: `Instance "${instanceId}" already exists` }] };
|
|
204
|
+
}
|
|
205
|
+
// Encrypt and persist
|
|
206
|
+
const toPersist = this.encryptValues(schema, result.values);
|
|
207
|
+
this.upsertStmt.run(pluginId, instanceId, JSON.stringify(schema), JSON.stringify(toPersist));
|
|
208
|
+
return result;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Delete a plugin instance and its config.
|
|
212
|
+
*/
|
|
213
|
+
deleteInstance(pluginId, instanceId) {
|
|
214
|
+
const result = this.deleteInstanceStmt.run(pluginId, instanceId);
|
|
215
|
+
if (result.changes === 0) {
|
|
216
|
+
throw new Error(`Instance not found: ${pluginId}::${instanceId}`);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Return decrypted stored values for a plugin, or undefined if no row exists.
|
|
221
|
+
* Used by the test-connection endpoint to resolve secret sentinels.
|
|
222
|
+
*/
|
|
223
|
+
getDecryptedValues(pluginId, instanceId = "default") {
|
|
224
|
+
const row = this.getByInstanceStmt.get(pluginId, instanceId);
|
|
225
|
+
if (!row)
|
|
226
|
+
return undefined;
|
|
227
|
+
const schema = JSON.parse(row.schema);
|
|
228
|
+
const storedValues = JSON.parse(row.values);
|
|
229
|
+
return this.decryptValues(schema, storedValues);
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Check whether a plugin needs configuration (required fields missing).
|
|
233
|
+
* Used by context.ts for the debug log.
|
|
234
|
+
*/
|
|
235
|
+
needsConfiguration(pluginId, instanceId = "default") {
|
|
236
|
+
const row = this.getByInstanceStmt.get(pluginId, instanceId);
|
|
237
|
+
if (!row)
|
|
238
|
+
return false;
|
|
239
|
+
const schema = JSON.parse(row.schema);
|
|
240
|
+
const storedValues = JSON.parse(row.values);
|
|
241
|
+
const decryptedValues = this.decryptValues(schema, storedValues);
|
|
242
|
+
return this.computeNeedsConfiguration(schema, decryptedValues);
|
|
243
|
+
}
|
|
244
|
+
// ── Private helpers ──────────────────────────────────────
|
|
245
|
+
buildDefaults(schema) {
|
|
246
|
+
const defaults = {};
|
|
247
|
+
for (const [key, rawField] of Object.entries(schema)) {
|
|
248
|
+
const field = rawField;
|
|
249
|
+
if (field.default !== undefined) {
|
|
250
|
+
defaults[key] = field.default;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
return defaults;
|
|
254
|
+
}
|
|
255
|
+
computeNeedsConfiguration(schema, values) {
|
|
256
|
+
for (const [key, rawField] of Object.entries(schema)) {
|
|
257
|
+
const field = rawField;
|
|
258
|
+
if (field.required === true) {
|
|
259
|
+
const val = values[key];
|
|
260
|
+
if (val === undefined || val === null || val === "") {
|
|
261
|
+
if (field.default === undefined) {
|
|
262
|
+
return true;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
return false;
|
|
268
|
+
}
|
|
269
|
+
encryptValues(schema, values) {
|
|
270
|
+
const result = { ...values };
|
|
271
|
+
for (const [key, rawField] of Object.entries(schema)) {
|
|
272
|
+
const field = rawField;
|
|
273
|
+
if (field.type === "secret" && result[key] !== undefined && result[key] !== null) {
|
|
274
|
+
const plaintext = String(result[key]);
|
|
275
|
+
result[key] = encrypt(plaintext);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
return result;
|
|
279
|
+
}
|
|
280
|
+
decryptValues(schema, values) {
|
|
281
|
+
const result = { ...values };
|
|
282
|
+
for (const [key, rawField] of Object.entries(schema)) {
|
|
283
|
+
const field = rawField;
|
|
284
|
+
if (field.type === "secret" && result[key] !== undefined && result[key] !== null && typeof result[key] === "string") {
|
|
285
|
+
try {
|
|
286
|
+
result[key] = decrypt(result[key]);
|
|
287
|
+
}
|
|
288
|
+
catch {
|
|
289
|
+
// If decryption fails, leave as-is (corrupted data)
|
|
290
|
+
this.logger.warn("failed to decrypt secret field", { field: key });
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return result;
|
|
295
|
+
}
|
|
296
|
+
maskSecretValues(schema, values) {
|
|
297
|
+
const result = { ...values };
|
|
298
|
+
for (const [key, rawField] of Object.entries(schema)) {
|
|
299
|
+
const field = rawField;
|
|
300
|
+
if (field.type === "secret") {
|
|
301
|
+
if (result[key] !== undefined && result[key] !== null && result[key] !== "") {
|
|
302
|
+
result[key] = SECRET_UNCHANGED_SENTINEL;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
return result;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
//# sourceMappingURL=config-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-service.js","sourceRoot":"","sources":["../../src/config/config-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ConfigSchema } from "../types/plugin.js";
|
|
2
|
+
export interface ValidationResult {
|
|
3
|
+
valid: boolean;
|
|
4
|
+
values: Record<string, unknown>;
|
|
5
|
+
errors: FieldError[];
|
|
6
|
+
}
|
|
7
|
+
export interface FieldError {
|
|
8
|
+
field: string;
|
|
9
|
+
message: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function validateConfig<T>(schema: ConfigSchema<T>, input: Record<string, unknown>): ValidationResult;
|
|
12
|
+
//# sourceMappingURL=validate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/config/validate.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAe,MAAM,oBAAoB,CAAC;AAEpE,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,MAAM,EAAE,UAAU,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AA6BD,wBAAgB,cAAc,CAAC,CAAC,EAC9B,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,EACvB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,gBAAgB,CA0GlB"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
// SPDX-License-Identifier: 0BSD
|
|
2
|
+
// SPDX-FileCopyrightText: 2025 Digi <digitalpaws@proton.me>
|
|
3
|
+
function isMissing(value) {
|
|
4
|
+
if (value === undefined || value === null)
|
|
5
|
+
return true;
|
|
6
|
+
if (typeof value === "string" && value === "")
|
|
7
|
+
return true;
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
function coerceNumber(value) {
|
|
11
|
+
if (typeof value === "number") {
|
|
12
|
+
if (Number.isFinite(value))
|
|
13
|
+
return { value, ok: true };
|
|
14
|
+
return { value: NaN, ok: false };
|
|
15
|
+
}
|
|
16
|
+
if (typeof value === "string") {
|
|
17
|
+
if (value === "")
|
|
18
|
+
return { value: NaN, ok: false };
|
|
19
|
+
const n = Number(value);
|
|
20
|
+
if (Number.isFinite(n))
|
|
21
|
+
return { value: n, ok: true };
|
|
22
|
+
return { value: NaN, ok: false };
|
|
23
|
+
}
|
|
24
|
+
return { value: NaN, ok: false };
|
|
25
|
+
}
|
|
26
|
+
function coerceBoolean(value) {
|
|
27
|
+
if (typeof value === "boolean")
|
|
28
|
+
return { value, ok: true };
|
|
29
|
+
if (value === "true")
|
|
30
|
+
return { value: true, ok: true };
|
|
31
|
+
if (value === "false")
|
|
32
|
+
return { value: false, ok: true };
|
|
33
|
+
return { value: false, ok: false };
|
|
34
|
+
}
|
|
35
|
+
export function validateConfig(schema, input) {
|
|
36
|
+
const values = {};
|
|
37
|
+
const errors = [];
|
|
38
|
+
for (const [key, rawField] of Object.entries(schema)) {
|
|
39
|
+
const field = rawField;
|
|
40
|
+
const inputVal = key in input ? input[key] : undefined;
|
|
41
|
+
const missing = isMissing(inputVal);
|
|
42
|
+
// Handle missing values
|
|
43
|
+
if (missing) {
|
|
44
|
+
if (field.default !== undefined) {
|
|
45
|
+
// Default is defined — use it (even if null)
|
|
46
|
+
values[key] = field.default;
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
if (field.required === true) {
|
|
50
|
+
errors.push({ field: key, message: "required field is missing" });
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
// Optional field with no default — store undefined
|
|
54
|
+
values[key] = undefined;
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
// Value is present — validate and coerce based on type
|
|
58
|
+
switch (field.type) {
|
|
59
|
+
case "string":
|
|
60
|
+
case "secret": {
|
|
61
|
+
if (typeof inputVal !== "string") {
|
|
62
|
+
errors.push({ field: key, message: `expected string, got ${typeof inputVal}` });
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
values[key] = inputVal;
|
|
66
|
+
}
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
case "number": {
|
|
70
|
+
const coerced = coerceNumber(inputVal);
|
|
71
|
+
if (!coerced.ok) {
|
|
72
|
+
errors.push({ field: key, message: `expected a finite number, got ${JSON.stringify(inputVal)}` });
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
values[key] = coerced.value;
|
|
76
|
+
}
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
case "boolean": {
|
|
80
|
+
const coerced = coerceBoolean(inputVal);
|
|
81
|
+
if (!coerced.ok) {
|
|
82
|
+
errors.push({ field: key, message: `expected boolean or "true"/"false", got ${JSON.stringify(inputVal)}` });
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
values[key] = coerced.value;
|
|
86
|
+
}
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
case "enum": {
|
|
90
|
+
if (typeof inputVal !== "string") {
|
|
91
|
+
errors.push({ field: key, message: `expected string for enum, got ${typeof inputVal}` });
|
|
92
|
+
}
|
|
93
|
+
else if (field.options && field.options.length > 0 && !field.options.includes(inputVal)) {
|
|
94
|
+
errors.push({
|
|
95
|
+
field: key,
|
|
96
|
+
message: `value "${inputVal}" is not in allowed options: [${field.options.join(", ")}]`,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
values[key] = inputVal;
|
|
101
|
+
}
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
case "url": {
|
|
105
|
+
if (typeof inputVal !== "string") {
|
|
106
|
+
errors.push({ field: key, message: `expected string for URL, got ${typeof inputVal}` });
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
try {
|
|
110
|
+
new URL(inputVal);
|
|
111
|
+
values[key] = inputVal;
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
errors.push({ field: key, message: `"${inputVal}" is not a valid URL` });
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
case "path": {
|
|
120
|
+
if (typeof inputVal !== "string" || inputVal === "") {
|
|
121
|
+
errors.push({ field: key, message: `expected non-empty string for path, got ${JSON.stringify(inputVal)}` });
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
values[key] = inputVal;
|
|
125
|
+
}
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
default:
|
|
129
|
+
// Unknown field type — leniently accept as-is
|
|
130
|
+
values[key] = inputVal;
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return {
|
|
135
|
+
valid: errors.length === 0,
|
|
136
|
+
values: errors.length === 0 ? values : {},
|
|
137
|
+
errors,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=validate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/config/validate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { ContentTypeDescriptor, NamingRule } from "@digitalpaws/starr-types-core";
|
|
2
|
+
/**
|
|
3
|
+
* Pure validation function. Returns null if valid, or an error message string.
|
|
4
|
+
*
|
|
5
|
+
* Checks in order:
|
|
6
|
+
* - id is truthy
|
|
7
|
+
* - name is truthy
|
|
8
|
+
* - identity is a non-empty array (DS2)
|
|
9
|
+
* - Each field: key, label, type are truthy
|
|
10
|
+
* - list fields must have itemType (FT2)
|
|
11
|
+
* - enum fields must have non-empty options (FT2)
|
|
12
|
+
*/
|
|
13
|
+
export declare function validateDescriptor(descriptor: ContentTypeDescriptor): string | null;
|
|
14
|
+
/**
|
|
15
|
+
* Full content-type registry implementation.
|
|
16
|
+
*
|
|
17
|
+
* Plugins call register() via ctx.contentTypes.register. The host internally
|
|
18
|
+
* uses get(), list(), and rollbackPlugin().
|
|
19
|
+
*/
|
|
20
|
+
export declare class ContentTypeRegistryImpl {
|
|
21
|
+
private descriptors;
|
|
22
|
+
private pluginToIds;
|
|
23
|
+
/**
|
|
24
|
+
* Register a descriptor. Optionally associates it with a pluginId for
|
|
25
|
+
* rollback support.
|
|
26
|
+
*/
|
|
27
|
+
register(descriptor: ContentTypeDescriptor, pluginId?: string): void;
|
|
28
|
+
/** Retrieve a descriptor by id. Returns undefined if not found. */
|
|
29
|
+
get(id: string): ContentTypeDescriptor | undefined;
|
|
30
|
+
/** List all registered descriptors. */
|
|
31
|
+
list(): ContentTypeDescriptor[];
|
|
32
|
+
/**
|
|
33
|
+
* Roll back (remove) all descriptors registered by a given plugin.
|
|
34
|
+
* Safe to call for unknown pluginIds (no-op).
|
|
35
|
+
*/
|
|
36
|
+
rollbackPlugin(pluginId: string): void;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Compute a deterministic content-identity string for an item.
|
|
40
|
+
*
|
|
41
|
+
* Algorithm:
|
|
42
|
+
* 1. Look up the descriptor for item.contentTypeId.
|
|
43
|
+
* 2. If descriptor exists: walk descriptor.identity in order. For each key,
|
|
44
|
+
* if item.externalIds[key] is present and non-empty, return
|
|
45
|
+
* "${contentTypeId}::${key}=${value}".
|
|
46
|
+
* 3. If no identity key is present in the item's externalIds (or descriptor
|
|
47
|
+
* not found): return "${contentTypeId}::undecidable::${sortedExternalIds}"
|
|
48
|
+
* where sortedExternalIds is all externalId pairs sorted by key.
|
|
49
|
+
*/
|
|
50
|
+
export declare function computeContentIdentity(item: {
|
|
51
|
+
contentTypeId: string;
|
|
52
|
+
externalIds: Record<string, string>;
|
|
53
|
+
}, registry: ContentTypeRegistryImpl): string;
|
|
54
|
+
/**
|
|
55
|
+
* Compare two content items for identity.
|
|
56
|
+
*
|
|
57
|
+
* Algorithm:
|
|
58
|
+
* 1. If contentTypeId differs → false (ID2).
|
|
59
|
+
* 2. Look up descriptor. If not found → false.
|
|
60
|
+
* 3. Walk descriptor.identity in order. For each key, if BOTH items have a
|
|
61
|
+
* truthy value, return whether they are equal.
|
|
62
|
+
* 4. No mutually-present key → false (ID1 — undecidable).
|
|
63
|
+
*/
|
|
64
|
+
export declare function areSameItem(a: {
|
|
65
|
+
contentTypeId: string;
|
|
66
|
+
externalIds: Record<string, string>;
|
|
67
|
+
}, b: {
|
|
68
|
+
contentTypeId: string;
|
|
69
|
+
externalIds: Record<string, string>;
|
|
70
|
+
}, registry: ContentTypeRegistryImpl): boolean;
|
|
71
|
+
/**
|
|
72
|
+
* Get the naming rule for a content type, falling back to a safe default.
|
|
73
|
+
*/
|
|
74
|
+
export declare function getNamingRule(contentTypeId: string, registry: ContentTypeRegistryImpl): NamingRule;
|
|
75
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/content-types/registry.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,qBAAqB,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAEvF;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,qBAAqB,GAAG,MAAM,GAAG,IAAI,CA6BnF;AAED;;;;;GAKG;AACH,qBAAa,uBAAuB;IAClC,OAAO,CAAC,WAAW,CAA4C;IAC/D,OAAO,CAAC,WAAW,CAA+B;IAElD;;;OAGG;IACH,QAAQ,CAAC,UAAU,EAAE,qBAAqB,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI;IAmBpE,mEAAmE;IACnE,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,qBAAqB,GAAG,SAAS;IAIlD,uCAAuC;IACvC,IAAI,IAAI,qBAAqB,EAAE;IAI/B;;;OAGG;IACH,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;CAOvC;AAaD;;;;;;;;;;;GAWG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,EACpE,QAAQ,EAAE,uBAAuB,GAChC,MAAM,CAcR;AAED;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CACzB,CAAC,EAAE;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,EACjE,CAAC,EAAE;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,EACjE,QAAQ,EAAE,uBAAuB,GAChC,OAAO,CAeT;AAUD;;GAEG;AACH,wBAAgB,aAAa,CAC3B,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,uBAAuB,GAChC,UAAU,CAGZ"}
|