@chengxianglibra/dsh-data-analysis 0.1.2 → 0.2.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.
Files changed (212) hide show
  1. package/README.md +136 -99
  2. package/cordis.patch.yml +1 -1
  3. package/lib/bin/environment.js +8 -6
  4. package/lib/client/credentials/changes.js +74 -0
  5. package/lib/client/credentials/create-datasource.js +126 -22
  6. package/lib/client/credentials/credential-draft.js +42 -0
  7. package/lib/client/credentials/defaults.js +11 -0
  8. package/lib/client/credentials/descriptions.js +46 -0
  9. package/lib/client/credentials/install.js +209 -81
  10. package/lib/client/credentials/model.js +323 -51
  11. package/lib/client/credentials/report-publishing.js +96 -0
  12. package/lib/client/credentials/styles.js +13 -9
  13. package/lib/client/i18n/context.js +22 -0
  14. package/lib/client/i18n/copy.js +57 -0
  15. package/lib/client/i18n/credentials.js +475 -0
  16. package/lib/client/i18n/host.js +27 -0
  17. package/lib/client/i18n/navigation.js +41 -0
  18. package/lib/client/i18n/presentation.js +577 -0
  19. package/lib/client/i18n/python.js +36 -0
  20. package/lib/client/i18n/semantic.js +441 -0
  21. package/lib/client/input-context.js +21 -0
  22. package/lib/client/python-tool/card.js +35 -0
  23. package/lib/client/python-tool/execution-error.js +35 -0
  24. package/lib/client/python-tool/install.js +9 -0
  25. package/lib/client/python-tool/model.js +101 -0
  26. package/lib/client/right-tabs/install.js +535 -0
  27. package/lib/client/right-tabs/live-delivery.js +58 -0
  28. package/lib/client/right-tabs/navigation.js +47 -0
  29. package/lib/client/right-tabs/page.js +178 -0
  30. package/lib/client/right-tabs/styles.js +11 -0
  31. package/lib/client/rpc.js +5 -0
  32. package/lib/client/semantic-reference.js +10 -0
  33. package/lib/client/workspace-header-action.js +14 -1
  34. package/lib/client/workspace-shortcuts.js +26 -0
  35. package/lib/client.js +365 -242
  36. package/lib/compatibility.js +1 -1
  37. package/lib/datasource/authoring.js +43 -4
  38. package/lib/datasource/bridge-programs.js +23 -20
  39. package/lib/datasource/bridge.js +46 -3
  40. package/lib/datasource/changes-contract.js +41 -0
  41. package/lib/datasource/changes.js +49 -0
  42. package/lib/datasource/configure.js +39 -0
  43. package/lib/datasource/defaults.js +25 -0
  44. package/lib/datasource/index.js +2 -1
  45. package/lib/datasource/python-options.js +21 -0
  46. package/lib/datasource/python.js +116 -25
  47. package/lib/datasource/rpc.js +75 -13
  48. package/lib/datasource/service.js +286 -11
  49. package/lib/datasource/test.js +2 -1
  50. package/lib/disclosure/activation.js +19 -7
  51. package/lib/disclosure/execution-guidance.js +52 -0
  52. package/lib/disclosure/help.js +4 -1
  53. package/lib/environment/runtime.js +35 -34
  54. package/lib/environment/workspace.js +18 -1
  55. package/lib/lifecycle.js +33 -0
  56. package/lib/plugin-agents.js +154 -0
  57. package/lib/plugin.js +94 -172
  58. package/lib/presentation/asset.js +34 -0
  59. package/lib/presentation/assets/portable.js +14 -14
  60. package/lib/presentation/assets/static.js +344 -73
  61. package/lib/presentation/build/index.js +3 -2
  62. package/lib/presentation/commit.js +19 -12
  63. package/lib/presentation/contracts/asset-path.js +9 -0
  64. package/lib/presentation/contracts/catalog.js +62 -0
  65. package/lib/presentation/contracts/charts.js +0 -23
  66. package/lib/presentation/contracts/index.js +17 -5
  67. package/lib/presentation/contracts/types.js +1 -1
  68. package/lib/presentation/delivery.js +45 -30
  69. package/lib/presentation/export-html.js +214 -0
  70. package/lib/presentation/files.js +4 -5
  71. package/lib/presentation/projection/index.js +2 -1
  72. package/lib/presentation/projection/program.js +2 -9
  73. package/lib/presentation/projection/sql-code.js +13 -13
  74. package/lib/presentation/reports.js +114 -17
  75. package/lib/presentation/rpc.js +96 -58
  76. package/lib/presentation/tool.js +6 -10
  77. package/lib/report-publishing/adapters.js +111 -0
  78. package/lib/report-publishing/config.js +69 -0
  79. package/lib/report-publishing/service.js +180 -0
  80. package/lib/rpc.js +49 -0
  81. package/lib/semantic-browser/contracts.js +17 -0
  82. package/lib/semantic-browser/program.js +10 -0
  83. package/lib/semantic-browser/service.js +40 -6
  84. package/lib/semantic-reference/bridge.js +10 -1
  85. package/lib/semantic-reference/contracts.js +20 -6
  86. package/lib/semantic-reference/environment.js +56 -0
  87. package/lib/semantic-reference/labels.js +16 -0
  88. package/lib/semantic-reference/rpc.js +39 -18
  89. package/lib/semantic-reference/search.js +5 -4
  90. package/lib/semantic-reference/usage.js +12 -3
  91. package/lib/tool-lifecycle.js +22 -0
  92. package/lib/types/client/credentials/changes.d.ts +9 -0
  93. package/lib/types/client/credentials/create-datasource.d.ts +3 -1
  94. package/lib/types/client/credentials/credential-draft.d.ts +14 -0
  95. package/lib/types/client/credentials/defaults.d.ts +2 -0
  96. package/lib/types/client/credentials/descriptions.d.ts +1 -0
  97. package/lib/types/client/credentials/install.d.ts +19 -1
  98. package/lib/types/client/credentials/model.d.ts +31 -11
  99. package/lib/types/client/credentials/report-publishing.d.ts +5 -0
  100. package/lib/types/client/credentials/styles.d.ts +1 -1
  101. package/lib/types/client/i18n/context.d.ts +17 -0
  102. package/lib/types/client/i18n/copy.d.ts +806 -0
  103. package/lib/types/client/i18n/credentials.d.ts +475 -0
  104. package/lib/types/client/i18n/host.d.ts +16 -0
  105. package/lib/types/client/i18n/navigation.d.ts +41 -0
  106. package/lib/types/client/i18n/presentation.d.ts +577 -0
  107. package/lib/types/client/i18n/python.d.ts +19 -0
  108. package/lib/types/client/i18n/semantic.d.ts +441 -0
  109. package/lib/types/client/input-context.d.ts +19 -0
  110. package/lib/types/client/presentation/ask-dsh.d.ts +4 -8
  111. package/lib/types/client/presentation/catalog-model.d.ts +24 -0
  112. package/lib/types/client/presentation/catalog.d.ts +20 -0
  113. package/lib/types/client/presentation/chart-tooltip.d.ts +2 -2
  114. package/lib/types/client/presentation/context-reference.d.ts +12 -0
  115. package/lib/types/client/presentation/copy-context.d.ts +2 -2
  116. package/lib/types/client/presentation/delivery-model.d.ts +27 -6
  117. package/lib/types/client/presentation/delivery.d.ts +8 -7
  118. package/lib/types/client/presentation/download.d.ts +2 -0
  119. package/lib/types/client/presentation/export-menu.d.ts +26 -0
  120. package/lib/types/client/presentation/export-view.d.ts +63 -0
  121. package/lib/types/client/presentation/host-entry.d.ts +8 -2
  122. package/lib/types/client/presentation/icons.d.ts +1 -1
  123. package/lib/types/client/presentation/model.d.ts +14 -15
  124. package/lib/types/client/presentation/reader.d.ts +15 -2
  125. package/lib/types/client/presentation/reference-source.d.ts +9 -0
  126. package/lib/types/client/presentation/source-code-model.d.ts +2 -1
  127. package/lib/types/client/presentation/source-code.d.ts +0 -5
  128. package/lib/types/client/presentation/source-dialog.d.ts +1 -2
  129. package/lib/types/client/presentation/sources.d.ts +5 -5
  130. package/lib/types/client/presentation/styles.d.ts +1 -1
  131. package/lib/types/client/presentation/table.d.ts +5 -1
  132. package/lib/types/client/python-tool/card.d.ts +2 -0
  133. package/lib/types/client/python-tool/execution-error.d.ts +3 -0
  134. package/lib/types/client/python-tool/install.d.ts +2 -0
  135. package/lib/types/client/python-tool/model.d.ts +20 -0
  136. package/lib/types/client/right-tabs/install.d.ts +18 -0
  137. package/lib/types/client/right-tabs/live-delivery.d.ts +8 -0
  138. package/lib/types/client/right-tabs/navigation.d.ts +17 -0
  139. package/lib/types/client/right-tabs/page.d.ts +36 -0
  140. package/lib/types/client/right-tabs/styles.d.ts +2 -0
  141. package/lib/types/client/rpc.d.ts +3 -0
  142. package/lib/types/client/semantic-reference.d.ts +4 -0
  143. package/lib/types/client/workspace-header-action.d.ts +1 -1
  144. package/lib/types/client/workspace-shortcuts.d.ts +15 -0
  145. package/lib/types/client.d.ts +6 -3
  146. package/lib/types/compatibility.d.ts +1 -1
  147. package/lib/types/datasource/authoring.d.ts +10 -0
  148. package/lib/types/datasource/bridge.d.ts +23 -2
  149. package/lib/types/datasource/changes-contract.d.ts +18 -0
  150. package/lib/types/datasource/changes.d.ts +15 -0
  151. package/lib/types/datasource/configure.d.ts +7 -0
  152. package/lib/types/datasource/defaults.d.ts +7 -0
  153. package/lib/types/datasource/index.d.ts +6 -1
  154. package/lib/types/datasource/python-options.d.ts +9 -0
  155. package/lib/types/datasource/python.d.ts +17 -2
  156. package/lib/types/datasource/rpc.d.ts +2 -1
  157. package/lib/types/datasource/service.d.ts +48 -5
  158. package/lib/types/datasource/test.d.ts +1 -1
  159. package/lib/types/disclosure/activation.d.ts +2 -1
  160. package/lib/types/disclosure/execution-guidance.d.ts +6 -0
  161. package/lib/types/disclosure/help.d.ts +1 -1
  162. package/lib/types/environment/types.d.ts +2 -1
  163. package/lib/types/environment/workspace.d.ts +1 -0
  164. package/lib/types/lifecycle.d.ts +8 -0
  165. package/lib/types/plugin-agents.d.ts +27 -0
  166. package/lib/types/plugin.d.ts +12 -28
  167. package/lib/types/presentation/asset.d.ts +6 -0
  168. package/lib/types/presentation/build/index.d.ts +7 -2
  169. package/lib/types/presentation/commit.d.ts +1 -1
  170. package/lib/types/presentation/contracts/asset-path.d.ts +2 -0
  171. package/lib/types/presentation/contracts/catalog.d.ts +27 -0
  172. package/lib/types/presentation/contracts/charts.d.ts +0 -2
  173. package/lib/types/presentation/contracts/types.d.ts +7 -4
  174. package/lib/types/presentation/delivery.d.ts +1 -1
  175. package/lib/types/presentation/export-html.d.ts +13 -0
  176. package/lib/types/presentation/reports.d.ts +6 -1
  177. package/lib/types/presentation/rpc.d.ts +3 -2
  178. package/lib/types/presentation/tool.d.ts +2 -2
  179. package/lib/types/report-publishing/adapters.d.ts +6 -0
  180. package/lib/types/report-publishing/config.d.ts +22 -0
  181. package/lib/types/report-publishing/service.d.ts +68 -0
  182. package/lib/types/rpc.d.ts +3 -0
  183. package/lib/types/semantic-browser/contracts.d.ts +7 -0
  184. package/lib/types/semantic-browser/service.d.ts +7 -1
  185. package/lib/types/semantic-reference/bridge.d.ts +1 -0
  186. package/lib/types/semantic-reference/contracts.d.ts +5 -2
  187. package/lib/types/semantic-reference/environment.d.ts +22 -0
  188. package/lib/types/semantic-reference/labels.d.ts +16 -0
  189. package/lib/types/semantic-reference/rpc.d.ts +1 -1
  190. package/lib/types/semantic-reference/search.d.ts +1 -1
  191. package/lib/types/tool-lifecycle.d.ts +4 -0
  192. package/lib/types/workspace-identity.d.ts +6 -0
  193. package/lib/workspace-identity.js +9 -0
  194. package/package.json +83 -61
  195. package/python/presentation-kit/dist/dsh_data_analysis_presentation_kit-1.1.0-py3-none-any.whl +0 -0
  196. package/skills/dsh-data-analysis-files/SKILL.md +32 -0
  197. package/skills/dsh-data-analysis-files/references/examples.md +93 -0
  198. package/skills/dsh-data-analysis-presentation/SKILL.md +10 -6
  199. package/skills/dsh-data-analysis-presentation/references/examples/artifact.draft.json +13 -4
  200. package/skills/dsh-data-analysis-presentation/references/examples/charts.draft.json +3 -2
  201. package/skills/dsh-data-analysis-presentation/references/examples/comparison.draft.json +3 -2
  202. package/skills/dsh-data-analysis-presentation/references/examples/computed-sources.draft.json +12 -5
  203. package/skills/dsh-data-analysis-presentation/references/examples/computed.draft.json +3 -2
  204. package/skills/dsh-data-analysis-presentation/references/examples/interaction.draft.json +3 -2
  205. package/skills/dsh-data-analysis-presentation/references/examples/source-only.draft.json +12 -4
  206. package/skills/dsh-data-analysis-presentation/references/examples.md +3 -1
  207. package/skills/dsh-data-analysis-presentation/references/export-html.md +7 -0
  208. package/skills/dsh-data-analysis-presentation/references/interaction.md +1 -1
  209. package/skills/dsh-data-analysis-presentation/references/narrative.md +1 -1
  210. package/skills/dsh-data-analysis-presentation/references/publishing.md +3 -0
  211. package/skills/dsh-data-analysis-presentation/references/schema.md +25 -1
  212. package/lib/types/client/presentation/install.d.ts +0 -15
@@ -1,21 +1,36 @@
1
+ import { credentialRevisionSchema } from "../../datasource/changes-contract.js";
2
+ import { PendingTasks } from "../../lifecycle.js";
3
+ import { CopyError, errorMessage, message } from "./../i18n/copy.js";
1
4
  const CHANNEL = '/dsh-data-analysis-credentials';
2
5
  const STORAGE_KEY = 'marivo-credential-operation';
3
6
  const messages = {
4
- 'context-changed': 'Workspace 或数据源定义已变化,请重新读取后操作。',
5
- 'credentials-changed': '凭证配置已变化,请重新读取后验证。',
6
- 'call-ended': '原调用已结束,已保存的值仍保留;请重新发起任务。',
7
- 'credential-missing': '凭证尚未配齐,请补填后验证。',
8
- 'credential-save-failed': '部分凭证保存失败;已成功保存的项目保留。',
9
- 'operation-busy': '该操作正在进行,请等待结果。',
10
- 'credential-state-unavailable': '暂时无法读取凭证状态,请稍后重试。',
11
- 'capacity-exceeded': '当前操作数量达到上限,请稍后重试。',
12
- 'datasource-already-exists': '该数据源已存在,请使用其他名称。',
13
- 'datasource-definition-invalid': '数据源定义无效,请检查名称、字段类型和凭证引用。',
14
- 'datasource-credential-ref-invalid': '凭证引用名称无效。*_env 字段填写引用名(如 MY_DB_PASSWORD),仅可使用字母、数字和下划线,且不能以数字开头;不能使用 MARIVO_、DSH_DATA_ANALYSIS_ 前缀或 Host 保留名称。实际用户名和密码请在创建后的“新增凭证”中填写。',
15
- 'datasource-authoring-unavailable': '当前 Runtime 不支持新增数据源。',
7
+ 'datasource-remove-unavailable': 'marivo.credentials.this-runtime-does-not-support-datasource-deletion',
8
+ 'datasource-not-removable': 'marivo.credentials.this-datasource-is-not-a-removable-project-local-definition',
9
+ 'datasource-remove-failed': 'marivo.credentials.datasource-deletion-is-unconfirmed-refresh-the-list-to-check',
10
+ 'credential-delete-failed': 'marivo.credentials.datasource-deleted-but-some-credentials-could-not-be-deleted',
11
+ 'datasource-config-changed': 'marivo.credentials.configuration-changed-reopen-the-editor-before-saving',
12
+ 'datasource-identity-fixed': 'marivo.credentials.datasource-name-and-engine-cannot-be-changed',
13
+ 'context-changed': 'marivo.credentials.workspace-or-datasource-definition-changed-reload-before-continuing',
14
+ 'credentials-changed': 'marivo.credentials.credential-configuration-changed-reload-before-validating',
15
+ 'call-ended': 'marivo.credentials.the-original-call-ended-saved-values-remain-start-the',
16
+ 'credential-missing': 'marivo.credentials.some-credentials-are-missing-complete-them-before-validating',
17
+ 'credential-save-failed': 'marivo.credentials.some-credentials-could-not-be-saved-successfully-saved-entries',
18
+ 'operation-busy': 'marivo.credentials.this-operation-is-in-progress-wait-for-its-result',
19
+ 'credential-state-unavailable': 'marivo.credentials.credential-status-is-temporarily-unavailable-retry-later',
20
+ 'capacity-exceeded': 'marivo.credentials.the-operation-limit-has-been-reached-retry-later',
21
+ 'datasource-already-exists': 'marivo.credentials.this-datasource-already-exists-choose-another-name',
22
+ 'datasource-definition-invalid': 'marivo.credentials.invalid-datasource-definition-check-the-name-field-types-and',
23
+ 'datasource-credential-ref-invalid': 'marivo.credentials.invalid-credential-reference-in-env-fields-enter-a-reference',
24
+ 'datasource-authoring-unavailable': 'marivo.credentials.this-runtime-does-not-support-datasource-creation',
25
+ 'datasource-defaults-invalid': 'marivo.credentials.invalid-datasourcedefaults-format-use-a-json-mapping-from-engines',
26
+ 'datasource-defaults-backend-invalid': 'marivo.credentials.datasourcedefaults-contains-an-engine-unsupported-by-the-current-runtime',
27
+ 'datasource-defaults-field-invalid': 'marivo.credentials.datasourcedefaults-contains-a-field-unsupported-by-the-current-runtime',
28
+ 'datasource-defaults-type-invalid': 'marivo.credentials.datasourcedefaults-field-types-do-not-match-the-current-runtime',
29
+ 'datasource-defaults-credential-forbidden': 'marivo.credentials.datasourcedefaults-does-not-support-credential-reference-fields-use-the',
16
30
  };
17
31
  export function credentialMessage(code) {
18
- return messages[code] ?? '凭证操作失败,请检查配置后重试。';
32
+ return (messages[code] ??
33
+ 'marivo.credentials.credential-operation-failed-check-the-configuration-and-retry');
19
34
  }
20
35
  class CredentialResponseError extends Error {
21
36
  code;
@@ -52,17 +67,21 @@ export class CredentialClientModel {
52
67
  operations: [],
53
68
  outcomes: {},
54
69
  };
70
+ #changes;
71
+ #watchTasks = new PendingTasks();
55
72
  #rpc;
56
73
  #storage;
57
74
  #listeners = new Set();
58
75
  #lifetime = new AbortController();
76
+ #suspended = false;
59
77
  #watch;
60
78
  #read;
61
79
  #refresh;
62
80
  #polls = new Map();
63
81
  #operations = new Map();
64
82
  #opened = new Set();
65
- constructor(rpc, storage) {
83
+ constructor(rpc, storage, changes) {
84
+ this.#changes = changes;
66
85
  this.#rpc = rpc;
67
86
  this.#storage = storage;
68
87
  }
@@ -86,12 +105,23 @@ export class CredentialClientModel {
86
105
  throw new CredentialResponseError(result.error?.message ?? '');
87
106
  return result.value;
88
107
  }
89
- show(workspaceId) {
108
+ async publishingCredentials(endpoint, workspaceId, payload, signal) {
109
+ const result = (await this.#rpc.call('/dsh-report-publishing', endpoint, { workspaceId, ...payload }, AbortSignal.any([signal, this.#lifetime.signal])));
110
+ if (!result.ok)
111
+ throw new Error(result.error?.message === 'report-publishing-config-changed'
112
+ ? 'report-publishing-config-changed'
113
+ : 'report-publishing-credential-operation-failed');
114
+ return result.value;
115
+ }
116
+ async show(workspaceId, selectedToken) {
117
+ this.#suspended = false;
90
118
  this.#patch({ open: true, requestId: '' });
91
- void this.selectWorkspace(workspaceId);
119
+ for (const entry of this.#operations.values())
120
+ void this.#query(entry.handle);
121
+ await this.selectWorkspace(workspaceId, selectedToken);
92
122
  }
93
- async authoring(workspaceId, signal) {
94
- return (await this.#call('authoring', { workspaceId }, signal));
123
+ async authoring(workspaceId, signal, mode = 'create') {
124
+ return (await this.#call('authoring', { workspaceId, mode }, signal));
95
125
  }
96
126
  async createDatasource(workspaceId, schema, input) {
97
127
  let result;
@@ -111,7 +141,9 @@ export class CredentialClientModel {
111
141
  'context-changed',
112
142
  ].includes(error.code))
113
143
  throw error;
114
- throw new Error(`${error instanceof Error ? error.message : '新增数据源失败。'} 如提交结果未确认,请刷新列表核对后再操作。`);
144
+ throw new CopyError(message('marivo.credentials.value-if-submission-is-unconfirmed-refresh-the-list-to', {
145
+ p0: error instanceof Error ? errorMessage(error) : 'marivo.credentials.create-failed',
146
+ }));
115
147
  }
116
148
  if (!this.#state.open || this.#state.workspaceId !== workspaceId)
117
149
  return;
@@ -122,11 +154,102 @@ export class CredentialClientModel {
122
154
  if (created)
123
155
  this.select(created.token);
124
156
  }
157
+ async configuration(workspaceId, name, signal) {
158
+ return (await this.#call('configuration', { workspaceId, name }, signal));
159
+ }
160
+ async saveConfiguration(workspaceId, schema, input, original, requestId, changes = {}) {
161
+ try {
162
+ let result;
163
+ try {
164
+ result = (await this.#call(original ? 'update-datasource' : 'create-datasource', {
165
+ workspaceId,
166
+ generation: schema.generation,
167
+ fingerprint: schema.fingerprint,
168
+ ...input,
169
+ ...(original ? { name: original.name, version: original.version } : {}),
170
+ ...(requestId ? { requestId } : {}),
171
+ }));
172
+ }
173
+ catch (error) {
174
+ if (error instanceof CredentialResponseError &&
175
+ [
176
+ 'datasource-credential-ref-invalid',
177
+ 'datasource-already-exists',
178
+ 'datasource-config-changed',
179
+ 'datasource-identity-fixed',
180
+ 'context-changed',
181
+ 'operation-busy',
182
+ ].includes(error.code))
183
+ throw error;
184
+ throw new CopyError(message('marivo.credentials.value-if-saving-is-unconfirmed-reload-the-configuration-to', {
185
+ p0: error instanceof Error ? errorMessage(error) : 'marivo.credentials.save-failed',
186
+ }));
187
+ }
188
+ if (!this.#state.open ||
189
+ this.#state.workspaceId !== workspaceId ||
190
+ (requestId && this.#state.requestId !== requestId))
191
+ return;
192
+ if (result.request) {
193
+ this.#acceptRequest(result.request);
194
+ const context = result.request.context;
195
+ if (context) {
196
+ this.#checkCredentialWrites(context, changes);
197
+ const ready = context.refs.every((ref) => context.credentials[ref]?.configured || changes[ref]);
198
+ if (ready || Object.keys(changes).length)
199
+ await this.start(context, 'submit', changes);
200
+ }
201
+ }
202
+ else {
203
+ await this.selectWorkspace(workspaceId);
204
+ if (this.#state.workspaceId !== workspaceId)
205
+ return;
206
+ const selected = this.#state.datasources.find((item) => item.name === result.name);
207
+ if (selected) {
208
+ this.select(selected.token);
209
+ this.#checkCredentialWrites(selected, changes);
210
+ if (Object.keys(changes).length)
211
+ await this.start(selected, 'save', changes);
212
+ }
213
+ }
214
+ }
215
+ finally {
216
+ for (const ref of Object.keys(changes))
217
+ delete changes[ref];
218
+ }
219
+ }
220
+ #checkCredentialWrites(context, changes) {
221
+ if (Object.keys(changes).some((ref) => context.credentials[ref]?.configured))
222
+ throw new Error('marivo.credentials.configuration-saved-but-the-reference-already-exists-existing-credentials');
223
+ }
224
+ #acceptRequest(request) {
225
+ this.#patch({
226
+ requests: [...this.#state.requests.filter((item) => item.id !== request.id), request],
227
+ });
228
+ }
229
+ async selectConfiguration(workspaceId, requestId, name) {
230
+ const request = (await this.#call('select-configuration', {
231
+ workspaceId,
232
+ requestId,
233
+ name,
234
+ }));
235
+ if (this.#state.workspaceId !== workspaceId ||
236
+ this.#state.requestId !== requestId ||
237
+ !this.#state.open)
238
+ return;
239
+ this.#acceptRequest(request);
240
+ }
125
241
  close() {
126
242
  this.#read?.abort();
127
243
  this.#refresh?.abort();
128
244
  this.#patch({ open: false, loading: false });
129
245
  }
246
+ suspend() {
247
+ this.#suspended = true;
248
+ for (const controller of this.#polls.values())
249
+ controller.abort();
250
+ this.#polls.clear();
251
+ this.close();
252
+ }
130
253
  openRequest(id) {
131
254
  this.#read?.abort();
132
255
  this.#refresh?.abort();
@@ -135,12 +258,12 @@ export class CredentialClientModel {
135
258
  select(token) {
136
259
  this.#patch({ selected: token, requestId: '', error: '' });
137
260
  }
138
- async selectWorkspace(workspaceId) {
261
+ async selectWorkspace(workspaceId, selectedToken) {
139
262
  this.#read?.abort();
140
263
  this.#refresh?.abort();
141
264
  const flight = new AbortController();
142
265
  this.#read = flight;
143
- const selected = this.#state.workspaceId === workspaceId ? this.#state.selected : '';
266
+ const selected = selectedToken ?? (this.#state.workspaceId === workspaceId ? this.#state.selected : '');
144
267
  const activity = new Map([...Object.values(this.#state.outcomes), ...this.#operations.values()].map((entry) => [
145
268
  entry.handle.scope,
146
269
  entry.handle.id,
@@ -173,10 +296,46 @@ export class CredentialClientModel {
173
296
  if (!flight.signal.aborted)
174
297
  this.#patch({
175
298
  loading: false,
176
- error: error instanceof Error ? error.message : '无法读取凭证状态。',
299
+ error: error instanceof Error
300
+ ? errorMessage(error)
301
+ : 'marivo.credentials.cannot-read-credential-status',
177
302
  });
178
303
  }
179
304
  }
305
+ /** Receive the shared Host watch without sharing a tab's selection or form state. */
306
+ syncRequests(sessionId, state) {
307
+ const requests = state.requests.filter((request) => request.sessionId === sessionId);
308
+ const changed = JSON.stringify(requests) !== JSON.stringify(this.#state.requests) ||
309
+ state.generation !== this.#state.generation ||
310
+ state.revision !== this.#state.revision;
311
+ this.#patch({ sessionId, requests, generation: state.generation, revision: state.revision });
312
+ if (changed && this.#state.open)
313
+ void this.refreshDatasources();
314
+ }
315
+ async refreshDatasources() {
316
+ const workspaceId = this.#state.workspaceId;
317
+ if (!workspaceId || !this.#state.open)
318
+ return;
319
+ this.#refresh?.abort();
320
+ const flight = new AbortController();
321
+ this.#refresh = flight;
322
+ try {
323
+ const value = (await this.#call('overview', { workspaceId }, flight.signal));
324
+ if (!flight.signal.aborted && this.#state.open && this.#state.workspaceId === workspaceId) {
325
+ const name = this.#state.datasources.find((item) => item.token === this.#state.selected)?.name;
326
+ this.#patch({
327
+ datasources: value.datasources,
328
+ generation: value.generation,
329
+ selected: value.datasources.find((item) => item.name === name)?.token ??
330
+ value.datasources[0]?.token ??
331
+ '',
332
+ });
333
+ }
334
+ }
335
+ catch {
336
+ /* Keep the visible form; explicit refresh can surface read failures. */
337
+ }
338
+ }
180
339
  session(sessionId) {
181
340
  if (sessionId === this.#state.sessionId && this.#watch)
182
341
  return;
@@ -187,30 +346,106 @@ export class CredentialClientModel {
187
346
  this.#watch = controller;
188
347
  this.#patch({ sessionId, requests: [], requestId: '', open: false });
189
348
  if (sessionId)
190
- void this.#watchSession(sessionId, controller.signal);
349
+ void this.#watchTasks.track(this.#watchSession(sessionId, controller.signal));
191
350
  }
192
- async #watchSession(sessionId, signal) {
193
- let cursor, attempt = 0;
194
- while (!signal.aborted && !this.#lifetime.signal.aborted) {
195
- try {
196
- const value = (await this.#call('watch', { sessionId, ...(cursor ? { cursor } : {}) }, signal));
197
- if (signal.aborted)
198
- return;
199
- cursor = value.cursor;
200
- attempt = 0;
201
- this.#patch({ generation: value.generation, requests: value.requests });
202
- const request = value.requests.find((r) => r.endedAt === undefined && !this.#opened.has(r.id));
203
- if (request) {
204
- this.#opened.add(request.id);
205
- this.openRequest(request.id);
351
+ async #watchSession(sessionId, caller) {
352
+ const lifetime = new AbortController();
353
+ const signal = AbortSignal.any([caller, lifetime.signal, this.#lifetime.signal]);
354
+ let feed;
355
+ let reading;
356
+ let pending;
357
+ const refresh = async () => {
358
+ let attempt = 0;
359
+ while (pending && !signal.aborted) {
360
+ const item = pending;
361
+ const readSignal = AbortSignal.any([signal, item.signal]);
362
+ if (readSignal.aborted) {
363
+ if (pending === item)
364
+ pending = undefined;
365
+ continue;
366
+ }
367
+ try {
368
+ // Never pass a cursor here: this is an immediate snapshot, not a long poll.
369
+ const value = (await this.#call('watch', { sessionId }, readSignal));
370
+ if (readSignal.aborted)
371
+ continue;
372
+ credentialRevisionSchema.parse({ generation: value.generation, cursor: value.cursor });
373
+ if (value.generation !== item.value.generation)
374
+ throw new Error('credential-generation-changed');
375
+ const requests = [...value.requests, ...(value.configurationRequests ?? [])];
376
+ this.#patch({
377
+ generation: value.generation,
378
+ revision: value.cursor,
379
+ requests,
380
+ ...([
381
+ 'marivo.credentials.credential-request-connection-interrupted-reconnecting-host-waits-remain-subject',
382
+ 'marivo.credentials.credential-request-notifications-are-unavailable-check-the-host-connection',
383
+ ].includes(String(this.#state.error))
384
+ ? { error: '' }
385
+ : {}),
386
+ });
387
+ item.accept();
388
+ if (pending === item ||
389
+ (pending?.signal === item.signal && pending.value.cursor === value.cursor)) {
390
+ pending?.accept();
391
+ pending = undefined;
392
+ }
393
+ attempt = 0;
394
+ const request = requests.find((r) => r.endedAt === undefined && !this.#opened.has(r.id));
395
+ if (request) {
396
+ this.#opened.add(request.id);
397
+ this.openRequest(request.id);
398
+ }
399
+ }
400
+ catch {
401
+ if (!readSignal.aborted) {
402
+ this.#patch({
403
+ error: 'marivo.credentials.credential-request-connection-interrupted-reconnecting-host-waits-remain-subject',
404
+ });
405
+ await delay([1000, 2000, 5000][Math.min(attempt++, 2)], readSignal);
406
+ }
206
407
  }
207
408
  }
208
- catch {
209
- if (!signal.aborted) {
210
- this.#patch({ error: '凭证待办连接中断,正在重连;Host 中的等待仍受原调用期限限制。' });
211
- await delay([1000, 2000, 5000][Math.min(attempt++, 2)], signal);
409
+ };
410
+ const startRead = () => {
411
+ if (reading || !pending || signal.aborted)
412
+ return;
413
+ reading = refresh().finally(() => {
414
+ reading = undefined;
415
+ startRead();
416
+ });
417
+ };
418
+ try {
419
+ if (!this.#changes)
420
+ throw new Error('credential-notification-unavailable');
421
+ feed = this.#changes(sessionId);
422
+ const stop = () => {
423
+ void feed?.dispose().catch(() => { });
424
+ };
425
+ signal.addEventListener('abort', stop, { once: true });
426
+ try {
427
+ for await (const item of feed) {
428
+ if (signal.aborted)
429
+ break;
430
+ credentialRevisionSchema.parse(item.value);
431
+ pending = item;
432
+ startRead();
212
433
  }
213
434
  }
435
+ finally {
436
+ signal.removeEventListener('abort', stop);
437
+ }
438
+ }
439
+ catch {
440
+ if (!signal.aborted)
441
+ this.#patch({
442
+ error: 'marivo.credentials.credential-request-notifications-are-unavailable-check-the-host-connection',
443
+ });
444
+ }
445
+ finally {
446
+ lifetime.abort();
447
+ await feed?.dispose().catch(() => { });
448
+ await reading;
214
449
  }
215
450
  }
216
451
  #persist() {
@@ -229,7 +464,7 @@ export class CredentialClientModel {
229
464
  }
230
465
  #visible(scope) {
231
466
  const selected = this.#state.requestId
232
- ? this.#state.requests.find((request) => request.id === this.#state.requestId)?.context.token
467
+ ? this.#state.requests.find((request) => request.id === this.#state.requestId)?.context?.token
233
468
  : this.#state.selected;
234
469
  return selected === scope;
235
470
  }
@@ -237,7 +472,9 @@ export class CredentialClientModel {
237
472
  const scopes = new Set(datasources.map((context) => context.token));
238
473
  return Object.fromEntries(Object.entries(this.#state.outcomes)
239
474
  .filter(([scope, entry]) => entry.handle.generation === generation &&
240
- (entry.workspaceId !== workspaceId || scopes.has(scope)))
475
+ (entry.workspaceId !== workspaceId ||
476
+ scopes.has(scope) ||
477
+ entry.operation?.action === 'delete-datasource'))
241
478
  .map(([scope, entry]) => [
242
479
  scope,
243
480
  scopes.has(scope) && activity.get(scope) === entry.handle.id
@@ -320,7 +557,12 @@ export class CredentialClientModel {
320
557
  if (entry)
321
558
  this.#patch({ handle: entry.handle, operation: entry.operation, error: entry.error ?? '' });
322
559
  }
323
- async start(context, action, changes = {}, reference) {
560
+ dismissOutcome(scope) {
561
+ const outcomes = { ...this.#state.outcomes };
562
+ delete outcomes[scope];
563
+ this.#patch({ outcomes });
564
+ }
565
+ async start(context, action, changes = {}, reference, deleteCredentials) {
324
566
  if ([...this.#operations.values()].some((entry) => entry.handle.scope === context.token &&
325
567
  !entry.error &&
326
568
  (!entry.operation || entry.operation.status === 'running'))) {
@@ -370,11 +612,31 @@ export class CredentialClientModel {
370
612
  ...(requestId ? { requestId } : {}),
371
613
  changes,
372
614
  ...(reference ? { reference } : {}),
615
+ ...(action === 'delete-datasource'
616
+ ? { deleteCredentials: deleteCredentials === true }
617
+ : {}),
373
618
  });
374
619
  }
375
- catch {
620
+ catch (error) {
621
+ if (action === 'delete-datasource' && error instanceof CredentialResponseError) {
622
+ const entry = this.#operations.get(handle.id);
623
+ if (entry)
624
+ this.#settle({
625
+ ...entry,
626
+ operation: {
627
+ ...operation,
628
+ status: 'failed',
629
+ phase: 'settled',
630
+ errors: [error.code],
631
+ },
632
+ });
633
+ await this.#refreshOverview(handle);
634
+ return;
635
+ }
376
636
  if (this.#state.handle?.id === handle.id && this.#visible(handle.scope))
377
- this.#patch({ error: '提交响应未确认,正在查询操作状态;不会重新发送秘密值。' });
637
+ this.#patch({
638
+ error: 'marivo.credentials.submission-response-unconfirmed-checking-operation-status-without-resending-secret',
639
+ });
378
640
  }
379
641
  finally {
380
642
  for (const ref of Object.keys(changes))
@@ -384,7 +646,8 @@ export class CredentialClientModel {
384
646
  }
385
647
  async #query(handle) {
386
648
  const entry = this.#operations.get(handle.id);
387
- if (!entry ||
649
+ if (this.#suspended ||
650
+ !entry ||
388
651
  entry.error ||
389
652
  this.#polls.has(handle.id) ||
390
653
  (entry.operation && entry.operation.status !== 'running'))
@@ -399,7 +662,9 @@ export class CredentialClientModel {
399
662
  if (signal.aborted)
400
663
  return;
401
664
  if (!operation) {
402
- const error = '操作状态不可恢复。保存可能已经发生,请重新读取实际配置后决定下一步。';
665
+ const error = entry.operation?.action === 'delete-datasource'
666
+ ? 'marivo.credentials.deletion-status-cannot-be-recovered-some-deletion-may-have'
667
+ : 'marivo.credentials.operation-status-cannot-be-recovered-saving-may-have-occurred';
403
668
  this.#settle({ ...entry, operation: undefined, error });
404
669
  return;
405
670
  }
@@ -413,7 +678,9 @@ export class CredentialClientModel {
413
678
  if (!signal.aborted &&
414
679
  this.#state.handle?.id === handle.id &&
415
680
  this.#visible(handle.scope))
416
- this.#patch({ error: '正在恢复操作结果;已提交的保存不会自动重发。' });
681
+ this.#patch({
682
+ error: 'marivo.credentials.recovering-operation-results-submitted-saves-will-not-be-resent',
683
+ });
417
684
  }
418
685
  await delay(1000, signal);
419
686
  }
@@ -458,7 +725,9 @@ export class CredentialClientModel {
458
725
  await this.#call('cancel-request', { requestId: id });
459
726
  }
460
727
  catch (error) {
461
- this.#patch({ error: error instanceof Error ? error.message : '调用已结束。' });
728
+ this.#patch({
729
+ error: error instanceof Error ? errorMessage(error) : 'marivo.credentials.the-call-has-ended',
730
+ });
462
731
  }
463
732
  }
464
733
  async cancelOperation(scope) {
@@ -473,7 +742,9 @@ export class CredentialClientModel {
473
742
  await this.#call('cancel-operation', handle);
474
743
  }
475
744
  catch {
476
- this.#patch({ error: '无法确认取消结果,请继续查询。' });
745
+ this.#patch({
746
+ error: 'marivo.credentials.cancellation-is-unconfirmed-continue-checking-status',
747
+ });
477
748
  }
478
749
  }
479
750
  reset() {
@@ -490,5 +761,6 @@ export class CredentialClientModel {
490
761
  for (const controller of this.#polls.values())
491
762
  controller.abort();
492
763
  this.#listeners.clear();
764
+ return this.#watchTasks.drain();
493
765
  }
494
766
  }
@@ -0,0 +1,96 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useEffect, useState } from 'react';
3
+ import { useCopy } from "./../i18n/context.js";
4
+ const labels = {
5
+ accessKeyId: 'Access Key ID',
6
+ secretAccessKey: 'Secret Access Key',
7
+ sessionToken: 'Session Token',
8
+ };
9
+ export function ReportPublishingCredentials({ model, workspaceId, }) {
10
+ const t = useCopy();
11
+ const [view, setView] = useState();
12
+ const [editing, setEditing] = useState({});
13
+ const [deleting, setDeleting] = useState();
14
+ const [draft, setDraft] = useState({});
15
+ const [busy, setBusy] = useState(false);
16
+ const [message, setMessage] = useState('');
17
+ const [lifetime] = useState(() => new AbortController());
18
+ useEffect(() => {
19
+ const abort = new AbortController();
20
+ void model
21
+ .publishingCredentials('describe', workspaceId, {}, abort.signal)
22
+ .then((next) => {
23
+ if (!abort.signal.aborted)
24
+ setView(next);
25
+ })
26
+ .catch(() => {
27
+ if (!abort.signal.aborted)
28
+ setMessage('marivo.credentials.cannot-read-report-publishing-credential-status-reopen-the-page');
29
+ });
30
+ return () => abort.abort();
31
+ }, [model, workspaceId]);
32
+ useEffect(() => () => lifetime.abort(), [lifetime]);
33
+ async function change(field, remove) {
34
+ setBusy(true);
35
+ setMessage('');
36
+ try {
37
+ const next = await model.publishingCredentials(remove ? 'unset' : 'set', workspaceId, {
38
+ field,
39
+ configId: view?.configId,
40
+ ...(remove ? {} : { value: draft[field] }),
41
+ }, lifetime.signal);
42
+ if (lifetime.signal.aborted)
43
+ return;
44
+ setView(next);
45
+ setEditing((previous) => ({ ...previous, [field]: false }));
46
+ setDeleting(undefined);
47
+ setDraft((previous) => ({ ...previous, [field]: '' }));
48
+ setMessage(remove
49
+ ? 'marivo.credentials.saved-value-deleted-status-refreshed'
50
+ : 'marivo.credentials.saved-the-next-publication-will-use-the-new-credential');
51
+ }
52
+ catch (error) {
53
+ if (!lifetime.signal.aborted)
54
+ setMessage(error instanceof Error && error.message === 'report-publishing-config-changed'
55
+ ? 'marivo.credentials.publishing-configuration-changed-refresh-credential-status-and-enter-values'
56
+ : 'marivo.credentials.credential-operation-unconfirmed-refresh-status-before-retrying-credentials-from');
57
+ }
58
+ finally {
59
+ if (!lifetime.signal.aborted)
60
+ setBusy(false);
61
+ }
62
+ }
63
+ if (!view?.enabled)
64
+ return message ? _jsx("p", { role: "status", children: t(message) }) : null;
65
+ return (_jsxs("section", { className: "mc-publishing", "aria-label": t('marivo.credentials.report-publishing-credentials'), children: [_jsxs("h3", { className: "mc-section-heading", children: [t('marivo.credentials.report-publishing-credentials-228'), view.name] }), _jsxs("p", { className: "mc-note", children: ["Bucket\uFF1A", view.bucket, t('marivo.credentials.credentials-are-shared-by-the-current-harness-across-all')] }), _jsx("div", { className: "mc-fields", children: view.fields.map((item) => (_jsxs("fieldset", { className: "mc-field", disabled: busy, "aria-label": item.reference, children: [_jsxs("div", { className: "mc-field-header", children: [_jsxs("div", { className: "mc-field-title", children: [_jsx("h4", { children: item.reference }), _jsxs("p", { children: [t('marivo.credentials.field'), labels[item.field], item.required ? t('marivo.credentials.required') : '', ' ', t('marivo.credentials.source'), item.source ?? t('marivo.credentials.none'), !item.writable && t('marivo.credentials.read-only-source')] })] }), _jsxs("div", { className: "mc-field-actions", children: [_jsx("span", { className: "mc-badge", "data-ready": item.configured, children: item.configured
66
+ ? t('marivo.credentials.configured')
67
+ : t('marivo.credentials.not-configured') }), item.writable && item.configured && (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", onClick: () => {
68
+ setEditing({ ...editing, [item.field]: !editing[item.field] });
69
+ setDraft({ ...draft, [item.field]: '' });
70
+ }, children: editing[item.field]
71
+ ? t('marivo.credentials.cancel-replacement')
72
+ : t('marivo.credentials.replace') }), _jsx("button", { type: "button", className: "mc-danger", onClick: () => setDeleting(item.field), children: t('marivo.credentials.delete-saved-value') })] }))] })] }), item.writable && (!item.configured || editing[item.field]) && (_jsxs("label", { className: "mc-secret-input", children: [t('marivo.credentials.new-value'), _jsx("input", { id: `report-publishing-${item.field}`, type: "password", autoComplete: "new-password", maxLength: 65536, value: draft[item.field] ?? '', placeholder: t('marivo.credentials.enter-credential-value-127'), onChange: (event) => setDraft({ ...draft, [item.field]: event.target.value }) }), _jsx("button", { type: "button", className: "mc-primary", disabled: !draft[item.field], onClick: () => void change(item.field, false), children: item.configured
73
+ ? t('marivo.credentials.confirm-replacement')
74
+ : t('marivo.credentials.add-credential') })] })), deleting === item.field && (_jsxs("div", { className: "mc-confirm", children: [_jsxs("p", { children: [t('marivo.credentials.confirm-deletion-of'), item.reference, " ", t('marivo.credentials.the-saved-value')] }), _jsx("button", { type: "button", className: "mc-danger", onClick: () => void change(item.field, true), children: t('marivo.credentials.confirm-saved-value-deletion') }), _jsx("button", { type: "button", onClick: () => setDeleting(undefined), children: t('marivo.credentials.keep') })] }))] }, item.field))) }), _jsx("button", { type: "button", disabled: busy, onClick: () => {
75
+ setBusy(true);
76
+ void model
77
+ .publishingCredentials('describe', workspaceId, {}, lifetime.signal)
78
+ .then((next) => {
79
+ if (!lifetime.signal.aborted) {
80
+ setView(next);
81
+ setDraft({});
82
+ setEditing({});
83
+ setDeleting(undefined);
84
+ setMessage('marivo.credentials.status-refreshed');
85
+ }
86
+ })
87
+ .catch(() => {
88
+ if (!lifetime.signal.aborted)
89
+ setMessage('marivo.credentials.refresh-failed-please-retry');
90
+ })
91
+ .finally(() => {
92
+ if (!lifetime.signal.aborted)
93
+ setBusy(false);
94
+ });
95
+ }, children: t('marivo.credentials.refresh-credential-status') }), message && _jsx("p", { role: "status", children: t(message) })] }));
96
+ }