dsh-edge 0.9.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/THIRD_PARTY_NOTICES.md +1318 -1075
- package/dist/assets/index-Df-65__b.js +110 -0
- package/dist/assets/index-b24khbeK.css +1 -0
- package/dist/assets/langs/{php-DDkx9sim.js → php-DJl5gRdb.js} +2 -2
- package/dist/assets/langs/{ruby-DCxyoPen.js → ruby-5eNB0pDK.js} +2 -2
- package/dist/assets/vendor-BNsW4eBh.css +1 -0
- package/dist/assets/vendor-CCJJTK99.js +411 -0
- package/dist/index.html +8 -8
- package/dist/plugins/@deepseek-ai/dsh-api-gateway/client.js +1513 -77
- package/dist/plugins/@deepseek-ai/dsh-api-remotes/client.js +3608 -757
- package/dist/plugins/@deepseek-ai/dsh-api-session-controller/client.js +2777 -0
- package/dist/plugins/@deepseek-ai/dsh-api-workspace-controller/client.js +415 -0
- package/dist/plugins/@deepseek-ai/dsh-client-connection/client.js +1975 -7466
- package/dist/plugins/@deepseek-ai/dsh-client-locale/client.js +225 -76
- package/dist/plugins/@deepseek-ai/dsh-client-modules/client.js +67 -15
- package/dist/plugins/@deepseek-ai/dsh-client-ui-agent-preset/client.js +270 -460
- package/dist/plugins/@deepseek-ai/dsh-client-ui-approval/client.js +292 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-attachment/client.js +42 -17
- package/dist/plugins/@deepseek-ai/dsh-client-ui-brand-official/client.js +7 -9
- package/dist/plugins/@deepseek-ai/dsh-client-ui-chat/client.js +8177 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-commands/client.js +22 -10
- package/dist/plugins/@deepseek-ai/dsh-client-ui-conversation/client.js +14950 -8910
- package/dist/plugins/@deepseek-ai/dsh-client-ui-deliverables/client.js +161 -137
- package/dist/plugins/@deepseek-ai/dsh-client-ui-goal/client.js +6 -7
- package/dist/plugins/@deepseek-ai/dsh-client-ui-input-trigger/client.js +268 -40
- package/dist/plugins/@deepseek-ai/dsh-client-ui-jobs/client.js +1 -1
- package/dist/plugins/@deepseek-ai/dsh-client-ui-layout/client.js +45 -11
- package/dist/plugins/@deepseek-ai/dsh-client-ui-message-feedback/client.js +30 -58
- package/dist/plugins/@deepseek-ai/dsh-client-ui-model-selection/client.js +188 -89
- package/dist/plugins/@deepseek-ai/dsh-client-ui-permission-presets/client.js +105 -75
- package/dist/plugins/@deepseek-ai/dsh-client-ui-renderer/client.js +650 -188
- package/dist/plugins/@deepseek-ai/dsh-client-ui-schedule/client.js +307 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-session/client.js +340 -0
- package/dist/plugins/@deepseek-ai/dsh-client-ui-settings/client.js +57 -50
- package/dist/plugins/@deepseek-ai/dsh-client-ui-settings-general/client.js +134 -69
- package/dist/plugins/@deepseek-ai/dsh-client-ui-settings-models/client.js +575 -442
- package/dist/plugins/@deepseek-ai/dsh-client-ui-sidebar/client.js +24 -11
- package/dist/plugins/@deepseek-ai/dsh-client-ui-skill/client.js +13 -9
- package/dist/plugins/@deepseek-ai/dsh-client-ui-subagent/client.js +44 -20
- package/dist/plugins/@deepseek-ai/dsh-client-ui-theme/client.js +265 -94
- package/dist/plugins/@deepseek-ai/dsh-client-ui-tool/client.js +899 -433
- package/dist/plugins/@deepseek-ai/dsh-client-ui-trajectory/client.js +1472 -613
- package/dist/plugins/@deepseek-ai/dsh-client-ui-user-questions/client.js +249 -95
- package/dist/plugins/@deepseek-ai/dsh-client-ui-workflow-run/client.js +5 -5
- package/dist/plugins/@deepseek-ai/dsh-client-ui-workspace/client.js +425 -146
- package/dist/plugins/@deepseek-ai/dsh-typert-registry/client.js +29 -14
- package/dist/plugins/dsh-edge-client-ui/client.js +28 -26
- package/package.json +62 -58
- package/scripts/verify-packed.mjs +1 -1
- package/worker/direct/index.js +2866 -1466
- package/worker/isolated/index.js +2438 -1038
- package/dist/assets/index-C6eRlFa6.css +0 -1
- package/dist/assets/index-ClqxG24t.js +0 -96
- package/dist/assets/vendor-CjyC-hUb.css +0 -1
- package/dist/assets/vendor-D22_Mp1f.js +0 -411
- package/dist/plugins/@deepseek-ai/dsh-client-runtime/client.js +0 -10574
|
@@ -7,7 +7,7 @@ window.__ModuleLoader__.load({
|
|
|
7
7
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
8
8
|
let react = require("react");
|
|
9
9
|
let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
10
|
-
let
|
|
10
|
+
let _deepseek_ai_dsh_client_store = require("@deepseek-ai/dsh-client-store");
|
|
11
11
|
//#region ../../../vendor/cosmokit/src/misc.ts
|
|
12
12
|
/** Return true when a value is `null` or `undefined`. */
|
|
13
13
|
function isNullable(value) {
|
|
@@ -806,9 +806,11 @@ window.__ModuleLoader__.load({
|
|
|
806
806
|
const LOCALE_SETTINGS_NAMESPACE = "locale";
|
|
807
807
|
/** Field carrying an explicit locale selection; absence delegates to the browser. */
|
|
808
808
|
const LOCALE_PREFERENCE_FIELD = "preference";
|
|
809
|
+
/** Accepted BCP 47-style language ids. */
|
|
810
|
+
const LOCALE_ID_PATTERN = /^[A-Za-z]{2,8}(?:-[A-Za-z0-9]{1,8})*$/u;
|
|
809
811
|
/** Locale identifiers shipped by the browser client. */
|
|
810
812
|
const LOCALE_IDS = ["zh", "en"];
|
|
811
|
-
Schema.object({ [LOCALE_PREFERENCE_FIELD]: Schema.
|
|
813
|
+
Schema.object({ [LOCALE_PREFERENCE_FIELD]: Schema.string().pattern(LOCALE_ID_PATTERN).required(false) });
|
|
812
814
|
//#endregion
|
|
813
815
|
//#region lib/types/locales/zh.js
|
|
814
816
|
/** zh base dictionary for the common namespace: cross-feature standard words. */
|
|
@@ -818,6 +820,13 @@ window.__ModuleLoader__.load({
|
|
|
818
820
|
"close": "关闭",
|
|
819
821
|
"copy": "复制",
|
|
820
822
|
"copied": "复制成功",
|
|
823
|
+
"copy.failed": "复制失败",
|
|
824
|
+
"copy.value": "复制值",
|
|
825
|
+
"copy.json": "复制 JSON",
|
|
826
|
+
"copy.path": "复制属性路径",
|
|
827
|
+
"copy.prettyJson": "复制格式化 JSON",
|
|
828
|
+
"copy.compactJson": "复制紧凑 JSON",
|
|
829
|
+
"copy.optionsHint": "{action};右键点击可选择复制方式",
|
|
821
830
|
"retry": "重试",
|
|
822
831
|
"loading": "加载中…",
|
|
823
832
|
"load.failed": "加载失败",
|
|
@@ -834,9 +843,17 @@ window.__ModuleLoader__.load({
|
|
|
834
843
|
"collapse": "收起",
|
|
835
844
|
"expand": "展开",
|
|
836
845
|
"back": "返回",
|
|
846
|
+
"brand.localBuild": "DSH 本地构建",
|
|
837
847
|
"unknown": "未知",
|
|
838
848
|
"none": "无",
|
|
839
|
-
"truncated": "已截断"
|
|
849
|
+
"truncated": "已截断",
|
|
850
|
+
"json.collapseNode": "收起 JSON 节点",
|
|
851
|
+
"json.expandNode": "展开 JSON 节点",
|
|
852
|
+
"json.label": "JSON",
|
|
853
|
+
"markdown.footnotes": "脚注",
|
|
854
|
+
"markdown.truncatedCharacters": "… 已截断,共 {total} 字符",
|
|
855
|
+
"number.thousand": "{value}K",
|
|
856
|
+
"number.million": "{value}M"
|
|
840
857
|
};
|
|
841
858
|
//#endregion
|
|
842
859
|
//#region lib/types/locales/en.js
|
|
@@ -847,6 +864,13 @@ window.__ModuleLoader__.load({
|
|
|
847
864
|
"close": "Close",
|
|
848
865
|
"copy": "Copy",
|
|
849
866
|
"copied": "Copied",
|
|
867
|
+
"copy.failed": "Copy failed",
|
|
868
|
+
"copy.value": "Copy value",
|
|
869
|
+
"copy.json": "Copy JSON",
|
|
870
|
+
"copy.path": "Copy property path",
|
|
871
|
+
"copy.prettyJson": "Copy pretty JSON",
|
|
872
|
+
"copy.compactJson": "Copy compact JSON",
|
|
873
|
+
"copy.optionsHint": "{action}; right-click for copy options",
|
|
850
874
|
"retry": "Retry",
|
|
851
875
|
"loading": "Loading…",
|
|
852
876
|
"load.failed": "Failed to load",
|
|
@@ -863,9 +887,17 @@ window.__ModuleLoader__.load({
|
|
|
863
887
|
"collapse": "Collapse",
|
|
864
888
|
"expand": "Expand",
|
|
865
889
|
"back": "Back",
|
|
890
|
+
"brand.localBuild": "DSH Local Build",
|
|
866
891
|
"unknown": "Unknown",
|
|
867
892
|
"none": "None",
|
|
868
|
-
"truncated": "Truncated"
|
|
893
|
+
"truncated": "Truncated",
|
|
894
|
+
"json.collapseNode": "Collapse JSON node",
|
|
895
|
+
"json.expandNode": "Expand JSON node",
|
|
896
|
+
"json.label": "JSON",
|
|
897
|
+
"markdown.footnotes": "Footnotes",
|
|
898
|
+
"markdown.truncatedCharacters": "… truncated at {total} characters",
|
|
899
|
+
"number.thousand": "{value}K",
|
|
900
|
+
"number.million": "{value}M"
|
|
869
901
|
};
|
|
870
902
|
//#endregion
|
|
871
903
|
//#region lib/types/locales/settings.js
|
|
@@ -876,7 +908,7 @@ window.__ModuleLoader__.load({
|
|
|
876
908
|
const en = { "language.title": "Language" };
|
|
877
909
|
//#endregion
|
|
878
910
|
//#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/locale/src/client/LanguageRow.module.css.mjs
|
|
879
|
-
const css = ".hVGvvW_row{border-bottom
|
|
911
|
+
const css = ".hVGvvW_row{border-bottom:.5px solid var(--dsw-alias-border-l2);align-items:center;gap:8px;padding:16px 0;display:flex}.hVGvvW_rowText{flex-direction:column;flex:1;gap:4px;min-width:0;padding-right:48px;display:flex}.hVGvvW_title{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:400;line-height:22px}.hVGvvW_selector{background:var(--dsw-alias-bg-module-platform);height:36px;font:inherit;color:var(--dsw-alias-label-primary);cursor:pointer;border:none;border-radius:18px;align-items:center;gap:12px;padding:0 14px;font-size:14px;line-height:22px;display:inline-flex}.hVGvvW_selector:hover{background:var(--dsw-alias-interactive-bg-hover)}.hVGvvW_chevron{flex:none}";
|
|
880
912
|
const tagId = "@deepseek-ai/dsh-client-locale/LanguageRow.module.css";
|
|
881
913
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
882
914
|
const tag = document.createElement("style");
|
|
@@ -959,7 +991,7 @@ window.__ModuleLoader__.load({
|
|
|
959
991
|
* @returns the store handle.
|
|
960
992
|
*/
|
|
961
993
|
function createLanguageRowStore() {
|
|
962
|
-
return (0,
|
|
994
|
+
return (0, _deepseek_ai_dsh_client_store.defineStore)({
|
|
963
995
|
init: () => ({
|
|
964
996
|
active: "",
|
|
965
997
|
options: [],
|
|
@@ -976,12 +1008,12 @@ window.__ModuleLoader__.load({
|
|
|
976
1008
|
//#endregion
|
|
977
1009
|
//#region lib/types/client/index.js
|
|
978
1010
|
/**
|
|
979
|
-
* English is both the locale the UI opens in when the browser names no
|
|
1011
|
+
* English is both the locale the UI opens in when the browser names no registered
|
|
980
1012
|
* language (and for non-browser runs), and the dictionary consulted after the
|
|
981
1013
|
* active locale misses a key. One constant serves both because the shipped
|
|
982
1014
|
* `zh`/`en` dictionaries carry identical key sets, so neither direction can
|
|
983
1015
|
* leave a key unresolved; the residual case points at English rather than
|
|
984
|
-
* zh because a browser naming
|
|
1016
|
+
* zh because a browser naming no registered language is the reader least
|
|
985
1017
|
* likely to read Chinese.
|
|
986
1018
|
*/
|
|
987
1019
|
const FALLBACK_LOCALE = "en";
|
|
@@ -989,55 +1021,65 @@ window.__ModuleLoader__.load({
|
|
|
989
1021
|
const COMMON_NS = "common";
|
|
990
1022
|
/** Namespace owning this feature's settings-row copy. */
|
|
991
1023
|
const SETTINGS_NS = "settings.locale";
|
|
992
|
-
/** The two shipped
|
|
993
|
-
const
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
label: "English"
|
|
999
|
-
}]);
|
|
1000
|
-
/**
|
|
1001
|
-
* `<html lang>` tag per shipped locale. The locale id is the app's own
|
|
1002
|
-
* vocabulary (primary subtag); the document attribute wants a BCP 47 tag,
|
|
1003
|
-
* which assistive technology and browser features (pronunciation rules,
|
|
1004
|
-
* translation offers, font fallback, spell check) read to pick their own
|
|
1005
|
-
* behavior. `zh` alone leaves the script ambiguous, so the shipped Chinese
|
|
1006
|
-
* copy names the variant it actually is.
|
|
1007
|
-
*/
|
|
1008
|
-
const DOCUMENT_LANGUAGE = {
|
|
1009
|
-
zh: "zh-CN",
|
|
1010
|
-
en: "en"
|
|
1024
|
+
/** The two locales and dictionaries shipped by this package. */
|
|
1025
|
+
const BUILT_IN_LOCALE_METADATA = {
|
|
1026
|
+
zh: {
|
|
1027
|
+
label: "中文",
|
|
1028
|
+
fallback: "en"
|
|
1029
|
+
},
|
|
1030
|
+
en: { label: "English" }
|
|
1011
1031
|
};
|
|
1032
|
+
const BUILT_IN_LOCALES = Object.freeze(LOCALE_IDS.map((id) => Object.freeze({
|
|
1033
|
+
id,
|
|
1034
|
+
...BUILT_IN_LOCALE_METADATA[id]
|
|
1035
|
+
})));
|
|
1036
|
+
/** Case-insensitive key for BCP 47-style ids. */
|
|
1037
|
+
function localeKey(value) {
|
|
1038
|
+
return value.toLowerCase();
|
|
1039
|
+
}
|
|
1040
|
+
/** Validate and detach a language-pack contribution from its mutable input. */
|
|
1041
|
+
function normalizeLanguage(input) {
|
|
1042
|
+
if (!LOCALE_ID_PATTERN.test(input.id)) throw new Error(`locale id "${input.id}" is not a BCP 47-style tag`);
|
|
1043
|
+
if (input.label.trim() === "") throw new Error("locale label must not be empty");
|
|
1044
|
+
if (!LOCALE_ID_PATTERN.test(input.fallback)) throw new Error(`locale fallback "${input.fallback}" is not a BCP 47-style tag`);
|
|
1045
|
+
return Object.freeze({
|
|
1046
|
+
id: input.id,
|
|
1047
|
+
label: input.label,
|
|
1048
|
+
fallback: input.fallback
|
|
1049
|
+
});
|
|
1050
|
+
}
|
|
1012
1051
|
/**
|
|
1013
|
-
* Point `<html lang>` at the active locale
|
|
1014
|
-
*
|
|
1015
|
-
*
|
|
1016
|
-
* @param active - the active locale id.
|
|
1052
|
+
* Point `<html lang>` at the active locale, keeping the served document in
|
|
1053
|
+
* sync with locale snapshot changes.
|
|
1054
|
+
* @param snapshot - current locale state, including the active definition.
|
|
1017
1055
|
*/
|
|
1018
|
-
function syncDocumentLanguage(
|
|
1056
|
+
function syncDocumentLanguage(snapshot) {
|
|
1019
1057
|
if (typeof document === "undefined") return;
|
|
1020
|
-
document.documentElement.lang =
|
|
1058
|
+
document.documentElement.lang = snapshot.active === "zh" ? "zh-CN" : snapshot.active;
|
|
1021
1059
|
}
|
|
1022
1060
|
/**
|
|
1023
|
-
* Dictionary registry plus locale preference. Lookup
|
|
1024
|
-
*
|
|
1025
|
-
* the shared common namespace
|
|
1026
|
-
*
|
|
1027
|
-
*
|
|
1028
|
-
*
|
|
1029
|
-
* LocaleFace getSnapshot/subscribe pair
|
|
1030
|
-
*
|
|
1061
|
+
* Dictionary registry plus locale preference. Lookup walks the active
|
|
1062
|
+
* language's declared fallback chain in the entry namespace, then repeats it
|
|
1063
|
+
* in the shared common namespace before showing the key itself. Reads go
|
|
1064
|
+
* through {@link getLocale}; preferences change only through
|
|
1065
|
+
* {@link setLocale}, while language packs extend the catalog through
|
|
1066
|
+
* {@link addLanguage}. Continuous sync uses the `locale/change` event or
|
|
1067
|
+
* the LocaleFace getSnapshot/subscribe pair installed through
|
|
1068
|
+
* `ctx.slots.installLocale`.
|
|
1031
1069
|
*/
|
|
1032
1070
|
var LocaleRuntime = class {
|
|
1033
1071
|
dicts = /* @__PURE__ */ new Map();
|
|
1034
1072
|
bound = /* @__PURE__ */ new Map();
|
|
1073
|
+
catalog = /* @__PURE__ */ new Map();
|
|
1074
|
+
fallbackChains = /* @__PURE__ */ new Map();
|
|
1035
1075
|
snapshot;
|
|
1036
1076
|
listeners = /* @__PURE__ */ new Set();
|
|
1037
1077
|
ctx;
|
|
1038
1078
|
host;
|
|
1039
1079
|
/** Browser-derived locale standing wherever no explicit Host selection does. */
|
|
1040
1080
|
provisional;
|
|
1081
|
+
/** Last explicit selection, including one awaiting an external registration. */
|
|
1082
|
+
preference;
|
|
1041
1083
|
/**
|
|
1042
1084
|
* @param ctx - owning context (change events are emitted on it; the scope
|
|
1043
1085
|
* listener is released through ctx.effect on dispose).
|
|
@@ -1047,10 +1089,12 @@ window.__ModuleLoader__.load({
|
|
|
1047
1089
|
constructor(ctx, host) {
|
|
1048
1090
|
this.ctx = ctx;
|
|
1049
1091
|
this.host = host;
|
|
1050
|
-
this.
|
|
1092
|
+
for (const locale of BUILT_IN_LOCALES) this.catalog.set(localeKey(locale.id), locale);
|
|
1093
|
+
const locales = this.localeList();
|
|
1094
|
+
this.provisional = resolveInitialLocale(locales);
|
|
1051
1095
|
this.snapshot = Object.freeze({
|
|
1052
1096
|
active: this.provisional,
|
|
1053
|
-
locales
|
|
1097
|
+
locales,
|
|
1054
1098
|
revision: 0
|
|
1055
1099
|
});
|
|
1056
1100
|
if (host !== void 0) {
|
|
@@ -1078,7 +1122,7 @@ window.__ModuleLoader__.load({
|
|
|
1078
1122
|
/**
|
|
1079
1123
|
* LocaleFace subscribe: notified on every snapshot change (locale switch
|
|
1080
1124
|
* or dictionary registration — registrations bump the revision so already
|
|
1081
|
-
* rendered outlets pick up late-arriving dictionaries).
|
|
1125
|
+
* rendered outlets pick up late-arriving dictionaries and locale definitions).
|
|
1082
1126
|
* @param fn - change callback.
|
|
1083
1127
|
* @returns unsubscribe.
|
|
1084
1128
|
*/
|
|
@@ -1101,12 +1145,49 @@ window.__ModuleLoader__.load({
|
|
|
1101
1145
|
* @param id - a registered locale id; unknown ids throw.
|
|
1102
1146
|
*/
|
|
1103
1147
|
setLocale(id) {
|
|
1104
|
-
const match = this.
|
|
1148
|
+
const match = this.catalog.get(localeKey(id));
|
|
1105
1149
|
if (match === void 0) throw new Error(`locale "${id}" is not registered`);
|
|
1150
|
+
this.preference = match.id;
|
|
1106
1151
|
if (this.snapshot.active !== match.id) this.publish(match.id, true);
|
|
1107
1152
|
this.host?.set(LOCALE_PREFERENCE_FIELD, match.id);
|
|
1108
1153
|
}
|
|
1109
1154
|
/**
|
|
1155
|
+
* Add one selectable language to the shared catalog. Its fallback must
|
|
1156
|
+
* already be registered, and following fallback definitions must terminate
|
|
1157
|
+
* at English. Dictionaries may register before or after this definition.
|
|
1158
|
+
* Registration rechecks an unresolved Host preference and the browser's
|
|
1159
|
+
* ordered language list. The caller owns the returned disposer; removing an
|
|
1160
|
+
* active language falls back without clearing the stored id.
|
|
1161
|
+
* @param input - stable id, self-described label, and fallback language id.
|
|
1162
|
+
* @returns idempotent disposer removing this exact definition.
|
|
1163
|
+
* @throws when fields are malformed, the id is occupied, or the fallback
|
|
1164
|
+
* target is unknown or creates a cycle.
|
|
1165
|
+
*/
|
|
1166
|
+
addLanguage(input) {
|
|
1167
|
+
const candidate = normalizeLanguage(input);
|
|
1168
|
+
const key = localeKey(candidate.id);
|
|
1169
|
+
if (this.catalog.has(key)) throw new Error(`locale "${candidate.id}" is already registered`);
|
|
1170
|
+
const fallback = this.catalog.get(localeKey(candidate.fallback));
|
|
1171
|
+
if (fallback === void 0) throw new Error(`locale fallback "${candidate.fallback}" is not registered`);
|
|
1172
|
+
const language = Object.freeze({
|
|
1173
|
+
...candidate,
|
|
1174
|
+
fallback: fallback.id
|
|
1175
|
+
});
|
|
1176
|
+
this.catalog.set(key, language);
|
|
1177
|
+
try {
|
|
1178
|
+
this.assertFallbackChain(language.id);
|
|
1179
|
+
} catch (error) {
|
|
1180
|
+
this.catalog.delete(key);
|
|
1181
|
+
throw error;
|
|
1182
|
+
}
|
|
1183
|
+
this.publishCatalog();
|
|
1184
|
+
return () => {
|
|
1185
|
+
if (this.catalog.get(key) !== language) return;
|
|
1186
|
+
this.catalog.delete(key);
|
|
1187
|
+
this.publishCatalog();
|
|
1188
|
+
};
|
|
1189
|
+
}
|
|
1190
|
+
/**
|
|
1110
1191
|
* Adopt the scope's accepted durable selection without writing it back; an
|
|
1111
1192
|
* absent selection returns to the browser-derived locale.
|
|
1112
1193
|
* @param host - the constructor-narrowed scope driving this adoption.
|
|
@@ -1114,19 +1195,74 @@ window.__ModuleLoader__.load({
|
|
|
1114
1195
|
adopt(host) {
|
|
1115
1196
|
const section = host.getSnapshot().value;
|
|
1116
1197
|
if (section === void 0) return;
|
|
1117
|
-
|
|
1198
|
+
this.preference = section.preference;
|
|
1199
|
+
const target = this.resolveActive();
|
|
1118
1200
|
if (this.snapshot.active === target) return;
|
|
1119
1201
|
this.publish(target, true);
|
|
1120
1202
|
}
|
|
1203
|
+
/** Recompute browser fallback and publish the current catalog. */
|
|
1204
|
+
publishCatalog() {
|
|
1205
|
+
this.fallbackChains.clear();
|
|
1206
|
+
const locales = this.localeList();
|
|
1207
|
+
this.provisional = resolveInitialLocale(locales);
|
|
1208
|
+
const active = this.resolveActive();
|
|
1209
|
+
this.publish(active, active !== this.snapshot.active, locales);
|
|
1210
|
+
}
|
|
1211
|
+
/** Resolve an explicit preference only while its definition is available. */
|
|
1212
|
+
resolveActive() {
|
|
1213
|
+
if (this.preference === void 0) return this.provisional;
|
|
1214
|
+
return this.catalog.get(localeKey(this.preference))?.id ?? this.provisional;
|
|
1215
|
+
}
|
|
1216
|
+
/** Snapshot the catalog in registration order. */
|
|
1217
|
+
localeList() {
|
|
1218
|
+
return Object.freeze([...this.catalog.values()]);
|
|
1219
|
+
}
|
|
1220
|
+
/** Fail a new definition whose complete fallback path does not reach English. */
|
|
1221
|
+
assertFallbackChain(start) {
|
|
1222
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1223
|
+
let current = this.catalog.get(localeKey(start));
|
|
1224
|
+
while (current !== void 0) {
|
|
1225
|
+
const key = localeKey(current.id);
|
|
1226
|
+
if (seen.has(key)) throw new Error(`locale fallback cycle includes "${current.id}"`);
|
|
1227
|
+
seen.add(key);
|
|
1228
|
+
if (key === localeKey("en")) return;
|
|
1229
|
+
/* v8 ignore next -- English is the only built-in terminal and every
|
|
1230
|
+
* language accepted by addLanguage has a required fallback. */
|
|
1231
|
+
if (current.fallback === void 0) throw new Error(`locale "${current.id}" fallback chain does not reach "en"`);
|
|
1232
|
+
const next = this.catalog.get(localeKey(current.fallback));
|
|
1233
|
+
if (next === void 0) throw new Error(`locale fallback "${current.fallback}" is not registered`);
|
|
1234
|
+
current = next;
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
/** Resolve a lookup chain, falling directly to English across an unload gap. */
|
|
1238
|
+
fallbackChain(start) {
|
|
1239
|
+
const startKey = localeKey(start);
|
|
1240
|
+
const cached = this.fallbackChains.get(startKey);
|
|
1241
|
+
if (cached !== void 0) return cached;
|
|
1242
|
+
const chain = [];
|
|
1243
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1244
|
+
let current = this.catalog.get(startKey);
|
|
1245
|
+
while (current !== void 0 && !seen.has(localeKey(current.id))) {
|
|
1246
|
+
const key = localeKey(current.id);
|
|
1247
|
+
seen.add(key);
|
|
1248
|
+
chain.push(current.id);
|
|
1249
|
+
current = current.fallback === void 0 ? void 0 : this.catalog.get(localeKey(current.fallback));
|
|
1250
|
+
}
|
|
1251
|
+
if (!seen.has(localeKey("en"))) chain.push("en");
|
|
1252
|
+
const resolved = Object.freeze(chain);
|
|
1253
|
+
this.fallbackChains.set(startKey, resolved);
|
|
1254
|
+
return resolved;
|
|
1255
|
+
}
|
|
1121
1256
|
register(ns, localeOrDicts, dict) {
|
|
1122
1257
|
const pairs = typeof localeOrDicts === "string" ? [[localeOrDicts, dict]] : Object.entries(localeOrDicts);
|
|
1258
|
+
for (const [locale] of pairs) if (!LOCALE_ID_PATTERN.test(locale)) throw new Error(`locale id "${locale}" is not a BCP 47-style tag`);
|
|
1123
1259
|
let locales = this.dicts.get(ns);
|
|
1124
1260
|
if (!locales) {
|
|
1125
1261
|
locales = /* @__PURE__ */ new Map();
|
|
1126
1262
|
this.dicts.set(ns, locales);
|
|
1127
1263
|
}
|
|
1128
|
-
for (const [locale] of pairs) if (locales.has(locale)) throw new Error(`locale namespace "${ns}" already has locale "${locale}"`);
|
|
1129
|
-
for (const [locale, entries] of pairs) locales.set(locale, entries);
|
|
1264
|
+
for (const [locale] of pairs) if (locales.has(localeKey(locale))) throw new Error(`locale namespace "${ns}" already has locale "${locale}"`);
|
|
1265
|
+
for (const [locale, entries] of pairs) locales.set(localeKey(locale), entries);
|
|
1130
1266
|
this.publish(this.snapshot.active, false);
|
|
1131
1267
|
return () => {
|
|
1132
1268
|
const owner = this.dicts.get(ns);
|
|
@@ -1134,9 +1270,12 @@ window.__ModuleLoader__.load({
|
|
|
1134
1270
|
* first register and never removed, so the disposer always finds it. */
|
|
1135
1271
|
if (!owner) return;
|
|
1136
1272
|
let removed = false;
|
|
1137
|
-
for (const [locale, entries] of pairs)
|
|
1138
|
-
|
|
1139
|
-
|
|
1273
|
+
for (const [locale, entries] of pairs) {
|
|
1274
|
+
const key = localeKey(locale);
|
|
1275
|
+
if (owner.get(key) === entries) {
|
|
1276
|
+
owner.delete(key);
|
|
1277
|
+
removed = true;
|
|
1278
|
+
}
|
|
1140
1279
|
}
|
|
1141
1280
|
if (removed) this.publish(this.snapshot.active, false);
|
|
1142
1281
|
};
|
|
@@ -1151,13 +1290,17 @@ window.__ModuleLoader__.load({
|
|
|
1151
1290
|
return t;
|
|
1152
1291
|
}
|
|
1153
1292
|
translate(ns, key, params) {
|
|
1154
|
-
const
|
|
1293
|
+
const chain = this.fallbackChain(this.snapshot.active);
|
|
1294
|
+
const template = this.lookup(ns, key, chain) ?? (ns !== "common" ? this.lookup("common", key, chain) : void 0) ?? key;
|
|
1155
1295
|
if (!params) return template;
|
|
1156
1296
|
return template.replace(/\{(\w+)\}/g, (match, name) => name in params ? String(params[name]) : match);
|
|
1157
1297
|
}
|
|
1158
|
-
lookup(ns, key) {
|
|
1298
|
+
lookup(ns, key, chain) {
|
|
1159
1299
|
const locales = this.dicts.get(ns);
|
|
1160
|
-
|
|
1300
|
+
for (const locale of chain) {
|
|
1301
|
+
const value = locales?.get(localeKey(locale))?.[key];
|
|
1302
|
+
if (value !== void 0) return value;
|
|
1303
|
+
}
|
|
1161
1304
|
}
|
|
1162
1305
|
/**
|
|
1163
1306
|
* Advance the snapshot revision and notify LocaleFace subscribers (render
|
|
@@ -1166,10 +1309,10 @@ window.__ModuleLoader__.load({
|
|
|
1166
1309
|
* registration-heavy boot cannot storm event listeners (which may
|
|
1167
1310
|
* re-register slots in response).
|
|
1168
1311
|
*/
|
|
1169
|
-
publish(active, localeChanged) {
|
|
1312
|
+
publish(active, localeChanged, locales = this.snapshot.locales) {
|
|
1170
1313
|
this.snapshot = Object.freeze({
|
|
1171
1314
|
active,
|
|
1172
|
-
locales
|
|
1315
|
+
locales,
|
|
1173
1316
|
revision: this.snapshot.revision + 1
|
|
1174
1317
|
});
|
|
1175
1318
|
if (localeChanged) this.ctx.emit("locale/change", this.snapshot);
|
|
@@ -1184,30 +1327,35 @@ window.__ModuleLoader__.load({
|
|
|
1184
1327
|
* The browser's own language wins over {@link FALLBACK_LOCALE}; an explicit
|
|
1185
1328
|
* Host preference may replace this provisional value after plugin activation.
|
|
1186
1329
|
*/
|
|
1187
|
-
function resolveInitialLocale() {
|
|
1188
|
-
return detectBrowserLocale() ?? "en";
|
|
1330
|
+
function resolveInitialLocale(locales) {
|
|
1331
|
+
return detectBrowserLocale(locales) ?? "en";
|
|
1189
1332
|
}
|
|
1190
1333
|
/**
|
|
1191
|
-
* The first
|
|
1192
|
-
*
|
|
1193
|
-
*
|
|
1194
|
-
*
|
|
1195
|
-
*
|
|
1196
|
-
*
|
|
1197
|
-
*
|
|
1334
|
+
* The first registered locale the browser asks for. Each browser tag first
|
|
1335
|
+
* matches a locale id exactly, then its primary subtag, so an exact regional
|
|
1336
|
+
* registration wins before a language-wide fallback.
|
|
1337
|
+
* `window` is the browser test, not `navigator`: Node exposes a global
|
|
1338
|
+
* `navigator` reporting the machine's own language, which must not decide the
|
|
1339
|
+
* locale for non-browser runs. `navigator.language` trails the ordered
|
|
1340
|
+
* `languages` list and covers hosts exposing only the single tag.
|
|
1341
|
+
* @param locales - definitions currently available to the browser.
|
|
1342
|
+
* @returns the first matching locale id, or undefined.
|
|
1198
1343
|
*/
|
|
1199
|
-
function detectBrowserLocale() {
|
|
1344
|
+
function detectBrowserLocale(locales) {
|
|
1200
1345
|
if (typeof window === "undefined") return void 0;
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
const
|
|
1204
|
-
|
|
1346
|
+
const languages = navigator.languages;
|
|
1347
|
+
for (const tag of [...languages ?? [], navigator.language]) {
|
|
1348
|
+
const requested = localeKey(tag);
|
|
1349
|
+
const exact = locales.find((locale) => localeKey(locale.id) === requested);
|
|
1350
|
+
if (exact !== void 0) return exact.id;
|
|
1351
|
+
const primary = requested.split("-")[0];
|
|
1352
|
+
const match = locales.find((locale) => localeKey(locale.id).split("-")[0] === primary);
|
|
1353
|
+
if (match !== void 0) return match.id;
|
|
1205
1354
|
}
|
|
1206
1355
|
}
|
|
1207
1356
|
/** Required services: slot registration plus the settings transport. */
|
|
1208
1357
|
const inject = [
|
|
1209
1358
|
"slots",
|
|
1210
|
-
"connection",
|
|
1211
1359
|
"remote",
|
|
1212
1360
|
"settingsScope"
|
|
1213
1361
|
];
|
|
@@ -1231,18 +1379,19 @@ window.__ModuleLoader__.load({
|
|
|
1231
1379
|
ctx.slots.installLocale(locale);
|
|
1232
1380
|
const store = createLanguageRowStore();
|
|
1233
1381
|
let bound;
|
|
1234
|
-
const sync = (
|
|
1235
|
-
|
|
1382
|
+
const sync = () => {
|
|
1383
|
+
const snapshot = locale.getSnapshot();
|
|
1384
|
+
syncDocumentLanguage(snapshot);
|
|
1236
1385
|
bound?.sync(snapshot.active, snapshot.locales.map((l) => ({
|
|
1237
1386
|
id: l.id,
|
|
1238
1387
|
label: l.label
|
|
1239
1388
|
})), snapshot.revision);
|
|
1240
1389
|
};
|
|
1241
|
-
ctx.
|
|
1242
|
-
|
|
1390
|
+
ctx.effect(() => locale.subscribe(sync), "locale: language row and document synchronization");
|
|
1391
|
+
sync();
|
|
1243
1392
|
const injected = (actions) => {
|
|
1244
1393
|
bound = actions;
|
|
1245
|
-
sync(
|
|
1394
|
+
sync();
|
|
1246
1395
|
return { setLocale: (id) => {
|
|
1247
1396
|
locale.setLocale(id);
|
|
1248
1397
|
} };
|
|
@@ -73,21 +73,26 @@ window.__ModuleLoader__.load({
|
|
|
73
73
|
const graph = wire;
|
|
74
74
|
if (typeof graph.rev !== "string") throw new Error("client-modules: boot manifest rev must be a string");
|
|
75
75
|
if (!Array.isArray(graph.entries)) throw new Error("client-modules: boot manifest entries must be an array");
|
|
76
|
-
|
|
76
|
+
if (!Array.isArray(graph.batches)) throw new Error("client-modules: boot manifest batches must be an array");
|
|
77
|
+
const moduleFields = [];
|
|
77
78
|
const plugins = [];
|
|
79
|
+
const seenEntryIds = /* @__PURE__ */ new Set();
|
|
78
80
|
for (const value of graph.entries) {
|
|
79
81
|
if (typeof value !== "object" || value === null) throw new Error("client-modules: boot manifest entry is not an object");
|
|
80
82
|
const row = value;
|
|
81
83
|
const where = typeof row.id === "string" ? `"${row.id}"` : JSON.stringify(row);
|
|
82
84
|
if (typeof row.id !== "string" || typeof row.url !== "string" || typeof row.rev !== "string") throw new Error(`client-modules: boot manifest entry ${where} must carry string id/url/rev`);
|
|
85
|
+
if (seenEntryIds.has(row.id)) throw new Error(`client-modules: duplicate graph entry "${row.id}"`);
|
|
86
|
+
seenEntryIds.add(row.id);
|
|
83
87
|
const subject = `boot manifest entry ${where}`;
|
|
84
88
|
const inject = optionalStringArray(subject, "inject", row.inject);
|
|
85
89
|
const external = optionalStringArray(subject, "external", row.external);
|
|
86
90
|
if (row.immediately !== void 0 && typeof row.immediately !== "boolean") throw new Error(`client-modules: boot manifest entry ${where} immediately must be a boolean`);
|
|
87
|
-
|
|
91
|
+
moduleFields.push({
|
|
88
92
|
id: row.id,
|
|
89
93
|
url: row.url,
|
|
90
94
|
rev: row.rev,
|
|
95
|
+
inject: inject === void 0 ? [] : [...inject],
|
|
91
96
|
external: external === void 0 ? [] : [...external]
|
|
92
97
|
});
|
|
93
98
|
plugins.push({
|
|
@@ -96,6 +101,33 @@ window.__ModuleLoader__.load({
|
|
|
96
101
|
immediately: row.immediately === true
|
|
97
102
|
});
|
|
98
103
|
}
|
|
104
|
+
const entryIds = new Set(moduleFields.map((row) => row.id));
|
|
105
|
+
const initialUrls = /* @__PURE__ */ new Map();
|
|
106
|
+
const batchUrls = /* @__PURE__ */ new Set();
|
|
107
|
+
for (const value of graph.batches) {
|
|
108
|
+
if (typeof value !== "object" || value === null) throw new Error("client-modules: boot manifest batch is not an object");
|
|
109
|
+
const batch = value;
|
|
110
|
+
const phase = batch.phase;
|
|
111
|
+
if (phase !== "bootstrap" && phase !== "application") throw new Error(`client-modules: boot manifest batch phase must be "bootstrap" or "application", received ${JSON.stringify(phase)}`);
|
|
112
|
+
if (typeof batch.url !== "string" || typeof batch.rev !== "string") throw new Error(`client-modules: boot manifest ${phase} batch must carry string url/rev`);
|
|
113
|
+
if (batchUrls.has(batch.url)) throw new Error(`client-modules: boot manifest carries duplicate batch URL ${JSON.stringify(batch.url)}`);
|
|
114
|
+
batchUrls.add(batch.url);
|
|
115
|
+
const entries = optionalStringArray(`boot manifest ${phase} batch`, "entries", batch.entries);
|
|
116
|
+
if (entries === void 0 || entries.length === 0) throw new Error(`client-modules: boot manifest ${phase} batch entries must be a non-empty string array`);
|
|
117
|
+
for (const id of entries) {
|
|
118
|
+
if (!entryIds.has(id)) throw new Error(`client-modules: boot manifest ${phase} batch names unknown entry "${id}"`);
|
|
119
|
+
if (initialUrls.has(id)) throw new Error(`client-modules: boot manifest entry "${id}" belongs to more than one batch`);
|
|
120
|
+
initialUrls.set(id, batch.url);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
const modules = moduleFields.map((row) => {
|
|
124
|
+
const initialUrl = initialUrls.get(row.id);
|
|
125
|
+
if (initialUrl === void 0) throw new Error(`client-modules: boot manifest entry "${row.id}" belongs to no initial-load batch`);
|
|
126
|
+
return {
|
|
127
|
+
...row,
|
|
128
|
+
initialUrl
|
|
129
|
+
};
|
|
130
|
+
});
|
|
99
131
|
return {
|
|
100
132
|
rev: graph.rev,
|
|
101
133
|
modules,
|
|
@@ -125,6 +157,11 @@ window.__ModuleLoader__.load({
|
|
|
125
157
|
}, { once: true });
|
|
126
158
|
document.head.append(el);
|
|
127
159
|
});
|
|
160
|
+
/** Replace the rev query while preserving absolute, protocol-relative, or path-relative form. */
|
|
161
|
+
function atRevision(url, rev) {
|
|
162
|
+
if (!/[?&]rev=[^&#]*/.test(url)) throw new Error(`client-modules: bundle URL ${url} has no revision`);
|
|
163
|
+
return url.replace(/([?&]rev=)[^&#]*/, `$1${encodeURIComponent(rev)}`);
|
|
164
|
+
}
|
|
128
165
|
/**
|
|
129
166
|
* Claim and inventory the <style> tags a factory injected during
|
|
130
167
|
* materialization: preset-emitted tags arrive pre-tagged with data-plugin;
|
|
@@ -151,8 +188,10 @@ window.__ModuleLoader__.load({
|
|
|
151
188
|
seed;
|
|
152
189
|
factories = /* @__PURE__ */ new Map();
|
|
153
190
|
bootstrapIds = /* @__PURE__ */ new Set();
|
|
154
|
-
/** In-flight
|
|
191
|
+
/** In-flight script transport per URL; every row in one batch shares it. */
|
|
155
192
|
pendingArrival = /* @__PURE__ */ new Map();
|
|
193
|
+
/** Single-resource combo URL selected by HMR after invalidating one row. */
|
|
194
|
+
reloadUrls = /* @__PURE__ */ new Map();
|
|
156
195
|
/** Materialization re-entrancy guard: factory-form CJS cannot deliver partial exports, so a cycle is fatal. */
|
|
157
196
|
materializing = /* @__PURE__ */ new Set();
|
|
158
197
|
graphRows = /* @__PURE__ */ new Map();
|
|
@@ -194,28 +233,38 @@ window.__ModuleLoader__.load({
|
|
|
194
233
|
}
|
|
195
234
|
/** Load one graph row so its factory is registered (idempotent per in-flight arrival). */
|
|
196
235
|
arrive(row) {
|
|
197
|
-
const { id
|
|
198
|
-
const pending = this.pendingArrival.get(id);
|
|
199
|
-
if (pending !== void 0) return pending;
|
|
236
|
+
const { id } = row;
|
|
200
237
|
if (this.loadCache.has(id) || this.factories.has(id)) return Promise.resolve();
|
|
201
|
-
const
|
|
238
|
+
const reloadUrl = this.reloadUrls.get(id);
|
|
239
|
+
const url = reloadUrl ?? row.initialUrl;
|
|
240
|
+
let transport = this.pendingArrival.get(url);
|
|
241
|
+
if (transport === void 0) {
|
|
242
|
+
transport = this.loadBundle(url).finally(() => {
|
|
243
|
+
this.pendingArrival.delete(url);
|
|
244
|
+
});
|
|
245
|
+
this.pendingArrival.set(url, transport);
|
|
246
|
+
}
|
|
247
|
+
return transport.then(() => {
|
|
202
248
|
if (!this.factories.has(id)) throw new Error(`client-modules: bundle ${url} loaded without registering "${id}" via __ModuleLoader__.load`);
|
|
203
|
-
|
|
204
|
-
this.pendingArrival.delete(id);
|
|
249
|
+
if (reloadUrl !== void 0 && this.reloadUrls.get(id) === reloadUrl) this.reloadUrls.delete(id);
|
|
205
250
|
});
|
|
206
|
-
this.pendingArrival.set(id, task);
|
|
207
|
-
return task;
|
|
208
251
|
}
|
|
209
|
-
/** Register each unresolved dynamic request before
|
|
210
|
-
async arriveGraphRow(row, open = []) {
|
|
252
|
+
/** Register each injected package and unresolved dynamic request before its consumer. */
|
|
253
|
+
async arriveGraphRow(row, open = [], visited = /* @__PURE__ */ new Set()) {
|
|
211
254
|
const cycleStart = open.indexOf(row.id);
|
|
212
255
|
if (cycleStart !== -1) throw new Error(`client-modules: module arrival cycle ${[...open.slice(cycleStart), row.id].join(" -> ")} (the host must reject this graph before serving it)`);
|
|
256
|
+
if (visited.has(row.id)) return;
|
|
257
|
+
visited.add(row.id);
|
|
213
258
|
const next = [...open, row.id];
|
|
214
259
|
for (const request of row.external) {
|
|
215
260
|
const id = stripClientSuffix(request);
|
|
216
261
|
if (this.seed.has(request) || this.loadCache.has(id)) continue;
|
|
217
262
|
const dependency = this.graphRows.get(id);
|
|
218
|
-
if (dependency !== void 0) await this.arriveGraphRow(dependency, next);
|
|
263
|
+
if (dependency !== void 0) await this.arriveGraphRow(dependency, next, visited);
|
|
264
|
+
}
|
|
265
|
+
for (const packageName of row.inject) {
|
|
266
|
+
const dependency = this.graphRows.get(packageName);
|
|
267
|
+
if (dependency !== void 0) await this.arriveGraphRow(dependency, [], visited);
|
|
219
268
|
}
|
|
220
269
|
await this.arrive(row);
|
|
221
270
|
}
|
|
@@ -276,9 +325,12 @@ window.__ModuleLoader__.load({
|
|
|
276
325
|
if (row === void 0) throw new Error(`client-modules: prefetch("${id}") — not a graph entry`);
|
|
277
326
|
await this.arriveGraphRow(row);
|
|
278
327
|
}
|
|
279
|
-
invalidate(id) {
|
|
328
|
+
invalidate(id, rev) {
|
|
280
329
|
const normalized = stripClientSuffix(id);
|
|
281
330
|
if (this.bootstrapIds.has(normalized)) return;
|
|
331
|
+
const row = this.graphRows.get(normalized);
|
|
332
|
+
if (row !== void 0) this.reloadUrls.set(normalized, atRevision(row.url, rev ?? row.rev));
|
|
333
|
+
else this.reloadUrls.delete(normalized);
|
|
282
334
|
this.factories.delete(normalized);
|
|
283
335
|
this.loadCache.delete(normalized);
|
|
284
336
|
}
|