@goodandready/dsh-dsml-artifact-guard 0.2.5 → 0.2.6
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/CHANGELOG.md +6 -0
- package/lib/client.js +66 -31
- package/lib/index.js +28 -12
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
+
## [0.2.6] - 2026-09-25
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- **SettingsForms Migration (DSH 0.1.7)**: marked Config schema as volatile and configured settings policy (`auto: false`) to suppress generic auto-generated form duplicates, while resolving row ID `dsh-dsml-artifact-guard` via client `configForms` with live subscription support (#17).
|
|
14
|
+
- **Client Locale Lifecycle**: wrapped client locale registration in `ctx.effect` to ensure clean namespace disposal on HMR and avoid collision on module reload (#44).
|
|
15
|
+
|
|
10
16
|
## [0.2.5] - 2026-09-24
|
|
11
17
|
|
|
12
18
|
### Fixed
|
package/lib/client.js
CHANGED
|
@@ -102,16 +102,20 @@ window.__ModuleLoader__.load({
|
|
|
102
102
|
});
|
|
103
103
|
|
|
104
104
|
const scopeRef = React.useRef(null);
|
|
105
|
-
if (!scopeRef.current && _ctx
|
|
105
|
+
if (!scopeRef.current && _ctx) {
|
|
106
106
|
try {
|
|
107
|
-
|
|
107
|
+
if (_ctx.configForms && typeof _ctx.configForms.get === 'function') {
|
|
108
|
+
scopeRef.current = _ctx.configForms.get(ROW_ID) || _ctx.configForms.get(NS);
|
|
109
|
+
} else if (_ctx.settingsScope && typeof _ctx.settingsScope.bind === 'function') {
|
|
110
|
+
scopeRef.current = _ctx.settingsScope.bind({ namespace: NS });
|
|
111
|
+
}
|
|
108
112
|
} catch (e) {
|
|
109
113
|
scopeRef.current = null;
|
|
110
114
|
}
|
|
111
115
|
}
|
|
112
116
|
const scope = scopeRef.current;
|
|
113
117
|
|
|
114
|
-
const currentLang = (_ctx?.locale?.
|
|
118
|
+
const currentLang = ((typeof _ctx?.locale?.getLocale === 'function' ? _ctx.locale.getLocale() : null) || (typeof navigator !== 'undefined' && navigator.language) || 'en').startsWith('zh') ? 'zh' : 'en';
|
|
115
119
|
const dict = currentLang === 'zh' ? zh : en;
|
|
116
120
|
const tt = (k, params) => {
|
|
117
121
|
let str = (typeof t === 'function' ? t(k) : null) || dict[k] || en[k] || k;
|
|
@@ -123,43 +127,67 @@ window.__ModuleLoader__.load({
|
|
|
123
127
|
return str;
|
|
124
128
|
};
|
|
125
129
|
|
|
126
|
-
// Load settings from snapshot
|
|
130
|
+
// Load settings from snapshot and subscribe to live changes
|
|
127
131
|
React.useEffect(() => {
|
|
128
132
|
if (!scope) {
|
|
129
133
|
setStatus('unavailable');
|
|
130
134
|
return;
|
|
131
135
|
}
|
|
132
136
|
let cancelled = false;
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
if (snap
|
|
138
|
-
if (snap.status === 'ready' && snap.value) {
|
|
139
|
-
setDraft({
|
|
140
|
-
mode: snap.value.mode || DEFAULTS.mode,
|
|
141
|
-
providerId: snap.value.providerId || DEFAULTS.providerId,
|
|
142
|
-
modelId: snap.value.modelId || DEFAULTS.modelId,
|
|
143
|
-
});
|
|
144
|
-
setStatus('ready');
|
|
145
|
-
} else {
|
|
146
|
-
setStatus(snap.status);
|
|
147
|
-
}
|
|
148
|
-
} else if (snap && typeof snap === 'object') {
|
|
137
|
+
const syncSnapshot = (snap) => {
|
|
138
|
+
if (!snap || cancelled) return;
|
|
139
|
+
const view = snap.view || snap.value || snap;
|
|
140
|
+
if (typeof snap === 'object' && 'status' in snap) {
|
|
141
|
+
if (snap.status === 'ready' && view) {
|
|
149
142
|
setDraft({
|
|
150
|
-
mode:
|
|
151
|
-
providerId:
|
|
152
|
-
modelId:
|
|
143
|
+
mode: view.mode || DEFAULTS.mode,
|
|
144
|
+
providerId: view.providerId || DEFAULTS.providerId,
|
|
145
|
+
modelId: view.modelId || DEFAULTS.modelId,
|
|
153
146
|
});
|
|
154
147
|
setStatus('ready');
|
|
155
148
|
} else {
|
|
156
|
-
setStatus(
|
|
149
|
+
setStatus(snap.status);
|
|
157
150
|
}
|
|
158
|
-
}
|
|
151
|
+
} else if (typeof view === 'object') {
|
|
152
|
+
setDraft({
|
|
153
|
+
mode: view.mode || DEFAULTS.mode,
|
|
154
|
+
providerId: view.providerId || DEFAULTS.providerId,
|
|
155
|
+
modelId: view.modelId || DEFAULTS.modelId,
|
|
156
|
+
});
|
|
157
|
+
setStatus('ready');
|
|
158
|
+
} else {
|
|
159
|
+
setStatus('ready');
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
(async () => {
|
|
164
|
+
try {
|
|
165
|
+
const snap = typeof scope.getSnapshot === 'function' ? scope.getSnapshot() : (typeof scope.get === 'function' ? await scope.get() : null);
|
|
166
|
+
syncSnapshot(snap);
|
|
167
|
+
} catch (err) {
|
|
159
168
|
if (!cancelled) setStatus('unavailable');
|
|
160
169
|
}
|
|
161
170
|
})();
|
|
162
|
-
|
|
171
|
+
|
|
172
|
+
let unsubscribe;
|
|
173
|
+
if (typeof scope.subscribe === 'function') {
|
|
174
|
+
try {
|
|
175
|
+
unsubscribe = scope.subscribe(() => {
|
|
176
|
+
try {
|
|
177
|
+
syncSnapshot(typeof scope.getSnapshot === 'function' ? scope.getSnapshot() : null);
|
|
178
|
+
} catch (err) {
|
|
179
|
+
/* Snapshot update read failed */
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
} catch (err) {
|
|
183
|
+
/* Subscription to scope updates unavailable */
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return () => {
|
|
188
|
+
cancelled = true;
|
|
189
|
+
if (typeof unsubscribe === 'function') unsubscribe();
|
|
190
|
+
};
|
|
163
191
|
}, [scope]);
|
|
164
192
|
|
|
165
193
|
// Check updater status on mount
|
|
@@ -179,7 +207,7 @@ window.__ModuleLoader__.load({
|
|
|
179
207
|
latestVersion: data.latestVersion || '',
|
|
180
208
|
updateAvailable: !!data.updateAvailable,
|
|
181
209
|
}));
|
|
182
|
-
} catch {
|
|
210
|
+
} catch (err) {
|
|
183
211
|
if (!cancelled) {
|
|
184
212
|
setUpdateState(s => ({ ...s, checking: false, error: tt('checkUpdateFailed') }));
|
|
185
213
|
}
|
|
@@ -544,10 +572,17 @@ window.__ModuleLoader__.load({
|
|
|
544
572
|
exports.inject = ['slots', 'configForms', 'locale'];
|
|
545
573
|
exports.apply = function apply(ctx) {
|
|
546
574
|
if (typeof ctx.locale?.register === 'function') {
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
575
|
+
const registerLocale = () => {
|
|
576
|
+
try {
|
|
577
|
+
return ctx.locale.register(NS, { en, zh });
|
|
578
|
+
} catch (err) {
|
|
579
|
+
/* Locale namespace already registered or unavailable */
|
|
580
|
+
}
|
|
581
|
+
};
|
|
582
|
+
if (typeof ctx.effect === 'function') {
|
|
583
|
+
ctx.effect(registerLocale, 'dsh-dsml-artifact-guard: locale');
|
|
584
|
+
} else {
|
|
585
|
+
registerLocale();
|
|
551
586
|
}
|
|
552
587
|
}
|
|
553
588
|
|
package/lib/index.js
CHANGED
|
@@ -9,12 +9,16 @@ const KEEP = 96;
|
|
|
9
9
|
export const name = '@goodandready/dsh-dsml-artifact-guard';
|
|
10
10
|
export const inject = ['llm', 'settings', 'webServer'];
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
const rawConfig = Schema.object({
|
|
13
13
|
mode: Schema.string().default('sanitize'),
|
|
14
14
|
providerId: Schema.string().default('opencode-go'),
|
|
15
15
|
modelId: Schema.string().default('deepseek-v4-flash'),
|
|
16
16
|
});
|
|
17
17
|
|
|
18
|
+
export const Config = typeof rawConfig.volatile === 'function'
|
|
19
|
+
? rawConfig.volatile()
|
|
20
|
+
: rawConfig.extra('volatile', true);
|
|
21
|
+
|
|
18
22
|
export function matchesScope(o, c) {
|
|
19
23
|
const provider = o?.providerId ?? o?.provider?.id ?? o?.provider;
|
|
20
24
|
const model = o?.modelId ?? o?.model?.id ?? o?.model?.name ?? o?.model;
|
|
@@ -109,19 +113,31 @@ export function apply(ctx, initialConfig = {}) {
|
|
|
109
113
|
|
|
110
114
|
let getConfig = () => activeConfig;
|
|
111
115
|
|
|
112
|
-
//
|
|
113
|
-
if (typeof ctx.
|
|
116
|
+
// Live config subscription from the plugin fiber
|
|
117
|
+
if (typeof ctx.on === 'function') {
|
|
118
|
+
ctx.effect(() => ctx.on('config', (nextConfig) => {
|
|
119
|
+
activeConfig = Config(nextConfig) ?? {};
|
|
120
|
+
}), 'dsh-dsml-artifact-guard: live config');
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// DSH 0.1.7 SettingsForms: suppress generic duplicate form, custom card mounts in plugins seat
|
|
124
|
+
if (ctx.settings && typeof ctx.settings.configure === 'function') {
|
|
125
|
+
ctx.effect(() => ctx.settings.configure({ auto: false }), 'dsh-dsml-artifact-guard: settings policy');
|
|
126
|
+
} else if (typeof ctx.inject === 'function') {
|
|
127
|
+
// Legacy settings service fallback
|
|
114
128
|
ctx.inject(['settings'], (sctx) => {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
129
|
+
if (typeof sctx.settings?.register === 'function') {
|
|
130
|
+
try {
|
|
131
|
+
const scope = sctx.settings.register(NS, Config, { base: initialConfig });
|
|
132
|
+
getConfig = () => Config(scope?.get() ?? initialConfig) ?? {};
|
|
133
|
+
if (typeof scope?.watch === 'function') {
|
|
134
|
+
sctx.effect(() => scope.watch(() => {
|
|
135
|
+
activeConfig = getConfig();
|
|
136
|
+
}));
|
|
137
|
+
}
|
|
138
|
+
} catch (err) {
|
|
139
|
+
/* Legacy settings service registration unavailable */
|
|
122
140
|
}
|
|
123
|
-
} catch (err) {
|
|
124
|
-
ctx.logger?.warn?.(`[dsh-dsml-artifact-guard] settings registration skipped: ${String(err)}`);
|
|
125
141
|
}
|
|
126
142
|
});
|
|
127
143
|
}
|