@linxin666/dsh-usage 0.4.2 → 0.4.3

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/lib/client.js CHANGED
@@ -2118,6 +2118,96 @@ window.__ModuleLoader__.load({
2118
2118
  };
2119
2119
  }
2120
2120
  //#endregion
2121
+ //#region src/client/settings-entry-form.ts
2122
+ /** The snapshot a form reports before the Host has answered with this entry. */
2123
+ function pendingSnapshot() {
2124
+ return {
2125
+ status: "loading",
2126
+ value: void 0,
2127
+ base: void 0,
2128
+ user: void 0,
2129
+ revision: void 0,
2130
+ writable: false,
2131
+ mode: "host"
2132
+ };
2133
+ }
2134
+ /**
2135
+ * Create a settings form bound to the profile entry id this Host serves, and
2136
+ * rebound whenever the shared describe mirror names a different one of this
2137
+ * package's rows.
2138
+ * @param options - the shared forms service and this package's candidate row ids.
2139
+ * @returns a form delegating to the currently resolved entry's own form.
2140
+ */
2141
+ function createServedEntryForm(options) {
2142
+ const { forms, entryIds } = options;
2143
+ const fallbackId = entryIds[0];
2144
+ const namespaceId = entryIds[entryIds.length - 1];
2145
+ const listeners = /* @__PURE__ */ new Set();
2146
+ let boundId;
2147
+ let bound;
2148
+ let offBound;
2149
+ let snapshot = pendingSnapshot();
2150
+ /** Republish: the delegated snapshot when one is bound, the pending one otherwise. */
2151
+ const publish = () => {
2152
+ if (bound !== void 0) snapshot = bound.getSnapshot();
2153
+ for (const listener of [...listeners]) listener();
2154
+ };
2155
+ /**
2156
+ * The entry id the mirror currently justifies. An unanswered or empty mirror
2157
+ * is not evidence of absence, so it keeps the first candidate; a mirror that
2158
+ * answers without any of this package's rows leaves the namespace itself.
2159
+ * @returns the entry id to bind.
2160
+ */
2161
+ const resolve = () => {
2162
+ let served;
2163
+ try {
2164
+ served = forms.describe().getSnapshot().view?.namespaces.map((row) => row.ns);
2165
+ } catch {
2166
+ served = void 0;
2167
+ }
2168
+ if (served === void 0 || served.length === 0) return fallbackId;
2169
+ return entryIds.find((id) => served.includes(id)) ?? namespaceId;
2170
+ };
2171
+ /** Bind (or rebind) the resolved entry's form; a no-op while it is unchanged. */
2172
+ const bind = () => {
2173
+ const target = resolve();
2174
+ if (target === boundId) return;
2175
+ offBound?.();
2176
+ offBound = void 0;
2177
+ let form;
2178
+ try {
2179
+ form = forms.get(target);
2180
+ } catch {
2181
+ boundId = void 0;
2182
+ return;
2183
+ }
2184
+ boundId = target;
2185
+ bound = form;
2186
+ offBound = form.subscribe(() => {
2187
+ publish();
2188
+ });
2189
+ publish();
2190
+ };
2191
+ try {
2192
+ forms.describe().subscribe(() => {
2193
+ bind();
2194
+ });
2195
+ } catch {}
2196
+ bind();
2197
+ return {
2198
+ getSnapshot: () => snapshot,
2199
+ subscribe: (listener) => {
2200
+ listeners.add(listener);
2201
+ return () => {
2202
+ listeners.delete(listener);
2203
+ };
2204
+ },
2205
+ set: (field, value) => bound?.set(field, value) ?? Promise.resolve(false),
2206
+ unset: (field) => bound?.unset(field) ?? Promise.resolve(false),
2207
+ mutate: (ops, expectedRevision) => bound?.mutate(ops, expectedRevision) ?? Promise.resolve(false)
2208
+ };
2209
+ }
2210
+ //#endregion
2121
2211
  //#region src/client/index.ts
2122
2212
  /** Hard ceiling for one usage API call; a stalled host must not pile up requests. */
2123
2213
  const USAGE_FETCH_TIMEOUT_MS = 2e4;
@@ -2133,8 +2223,17 @@ window.__ModuleLoader__.load({
2133
2223
  overview: () => usageFetch("api/dsh-usage/overview", "GET"),
2134
2224
  refresh: () => usageFetch("api/dsh-usage/refresh", "POST")
2135
2225
  };
2136
- /** Settings namespace the section edits (dsh-web-settings maps it onto this row's profile entry id). */
2226
+ /**
2227
+ * Settings namespace the section edits: the family identity of this plugin's
2228
+ * own settings form, and the row id a standalone bundle install carries.
2229
+ */
2137
2230
  const USAGE_SETTINGS_NS = "dsh-usage";
2231
+ /** Profile entry ids this package's patch rows carry, most likely first. */
2232
+ const USAGE_ENTRY_IDS = [
2233
+ "web-ui-usage",
2234
+ "usage",
2235
+ USAGE_SETTINGS_NS
2236
+ ];
2138
2237
  /** First-level nav position: directly below the Workshop section (order 150). */
2139
2238
  const SECTION_ORDER = 151;
2140
2239
  /** Required services. */
@@ -2163,7 +2262,10 @@ window.__ModuleLoader__.load({
2163
2262
  }
2164
2263
  }, "dsh-usage: dictionaries");
2165
2264
  const binder = ctx.get("webUiSettings");
2166
- const settingsForm = binder !== void 0 ? binder.bind({ namespace: USAGE_SETTINGS_NS }) : ctx.configForms.get(USAGE_SETTINGS_NS);
2265
+ const settingsForm = binder !== void 0 ? binder.bind({ namespace: USAGE_SETTINGS_NS }) : createServedEntryForm({
2266
+ forms: ctx.configForms,
2267
+ entryIds: USAGE_ENTRY_IDS
2268
+ });
2167
2269
  const store = createUsageStore().create();
2168
2270
  let pollSeq = 0;
2169
2271
  const poll = () => {