@chenchaolong/plugin-trade-compliance-workbench 1.0.1 → 1.0.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.
Files changed (196) hide show
  1. package/README.md +46 -1
  2. package/dist/assets/branding/yuneec-logo.png +0 -0
  3. package/dist/assets/templates/CI.xlsx +0 -0
  4. package/dist/assets/templates/CUSTOMS_DECLARATION.xlsx +0 -0
  5. package/dist/assets/templates/PI.xlsx +0 -0
  6. package/dist/assets/templates/PL.xlsx +0 -0
  7. package/dist/index.d.ts +14 -0
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/lib/adapters/file-storage.d.ts +4 -2
  10. package/dist/lib/adapters/file-storage.d.ts.map +1 -1
  11. package/dist/lib/adapters/file-storage.js +25 -10
  12. package/dist/lib/adapters/file-storage.js.map +1 -1
  13. package/dist/lib/adapters/hsbianma.provider.d.ts +1 -0
  14. package/dist/lib/adapters/hsbianma.provider.d.ts.map +1 -1
  15. package/dist/lib/adapters/hsbianma.provider.js +32 -3
  16. package/dist/lib/adapters/hsbianma.provider.js.map +1 -1
  17. package/dist/lib/adapters/translation.provider.d.ts +3 -0
  18. package/dist/lib/adapters/translation.provider.d.ts.map +1 -1
  19. package/dist/lib/adapters/translation.provider.js +15 -6
  20. package/dist/lib/adapters/translation.provider.js.map +1 -1
  21. package/dist/lib/constants.d.ts +1 -0
  22. package/dist/lib/constants.d.ts.map +1 -1
  23. package/dist/lib/constants.js +1 -0
  24. package/dist/lib/constants.js.map +1 -1
  25. package/dist/lib/demo-data.definitions.d.ts +28 -0
  26. package/dist/lib/demo-data.definitions.d.ts.map +1 -0
  27. package/dist/lib/demo-data.definitions.js +236 -0
  28. package/dist/lib/demo-data.definitions.js.map +1 -0
  29. package/dist/lib/demo-data.entity.d.ts +9 -0
  30. package/dist/lib/demo-data.entity.d.ts.map +1 -0
  31. package/dist/lib/demo-data.entity.js +24 -0
  32. package/dist/lib/demo-data.entity.js.map +1 -0
  33. package/dist/lib/demo-data.ids.d.ts +4 -0
  34. package/dist/lib/demo-data.ids.d.ts.map +1 -0
  35. package/dist/lib/demo-data.ids.js +19 -0
  36. package/dist/lib/demo-data.ids.js.map +1 -0
  37. package/dist/lib/demo-data.service.d.ts +26 -0
  38. package/dist/lib/demo-data.service.d.ts.map +1 -0
  39. package/dist/lib/demo-data.service.js +124 -0
  40. package/dist/lib/demo-data.service.js.map +1 -0
  41. package/dist/lib/domain/default-settings.d.ts +1 -0
  42. package/dist/lib/domain/default-settings.d.ts.map +1 -1
  43. package/dist/lib/domain/default-settings.js +1 -0
  44. package/dist/lib/domain/default-settings.js.map +1 -1
  45. package/dist/lib/domain/risk-notice.d.ts +59 -0
  46. package/dist/lib/domain/risk-notice.d.ts.map +1 -0
  47. package/dist/lib/domain/risk-notice.js +30 -0
  48. package/dist/lib/domain/risk-notice.js.map +1 -0
  49. package/dist/lib/entities/catalog.entity.d.ts.map +1 -1
  50. package/dist/lib/entities/catalog.entity.js +4 -2
  51. package/dist/lib/entities/catalog.entity.js.map +1 -1
  52. package/dist/lib/entities/customer.entity.d.ts +9 -2
  53. package/dist/lib/entities/customer.entity.d.ts.map +1 -1
  54. package/dist/lib/entities/customer.entity.js +29 -1
  55. package/dist/lib/entities/customer.entity.js.map +1 -1
  56. package/dist/lib/entities/index.d.ts +2 -2
  57. package/dist/lib/entities/index.d.ts.map +1 -1
  58. package/dist/lib/entities/index.js +2 -2
  59. package/dist/lib/entities/index.js.map +1 -1
  60. package/dist/lib/entities/procurement.entity.d.ts +6 -4
  61. package/dist/lib/entities/procurement.entity.d.ts.map +1 -1
  62. package/dist/lib/entities/procurement.entity.js +9 -1
  63. package/dist/lib/entities/procurement.entity.js.map +1 -1
  64. package/dist/lib/entities/settings.entity.d.ts +12 -2
  65. package/dist/lib/entities/settings.entity.d.ts.map +1 -1
  66. package/dist/lib/entities/settings.entity.js +40 -0
  67. package/dist/lib/entities/settings.entity.js.map +1 -1
  68. package/dist/lib/entities/shared.entity.d.ts +10 -5
  69. package/dist/lib/entities/shared.entity.d.ts.map +1 -1
  70. package/dist/lib/entities/shared.entity.js +22 -1
  71. package/dist/lib/entities/shared.entity.js.map +1 -1
  72. package/dist/lib/remote-components/trade-compliance-workbench/app.css +1 -0
  73. package/dist/lib/remote-components/trade-compliance-workbench/app.js +63 -277
  74. package/dist/lib/remote-components/trade-compliance-workbench/editor-source.mjs +2 -1
  75. package/dist/lib/remote-components/trade-compliance-workbench/editor.js +171 -128
  76. package/dist/lib/remote-ui/analytics-contracts.d.ts +106 -0
  77. package/dist/lib/remote-ui/analytics-contracts.d.ts.map +1 -0
  78. package/dist/lib/remote-ui/analytics-contracts.js +4 -0
  79. package/dist/lib/remote-ui/analytics-contracts.js.map +1 -0
  80. package/dist/lib/remote-ui/app-source.d.ts +2 -0
  81. package/dist/lib/remote-ui/app-source.d.ts.map +1 -0
  82. package/dist/lib/remote-ui/app-source.js +796 -0
  83. package/dist/lib/remote-ui/app-source.js.map +1 -0
  84. package/dist/lib/remote-ui/bridge.d.ts +36 -0
  85. package/dist/lib/remote-ui/bridge.d.ts.map +1 -0
  86. package/dist/lib/remote-ui/bridge.js +159 -0
  87. package/dist/lib/remote-ui/bridge.js.map +1 -0
  88. package/dist/lib/remote-ui/components/data-grid.d.ts +34 -0
  89. package/dist/lib/remote-ui/components/data-grid.d.ts.map +1 -0
  90. package/dist/lib/remote-ui/components/data-grid.js +59 -0
  91. package/dist/lib/remote-ui/components/data-grid.js.map +1 -0
  92. package/dist/lib/remote-ui/components/forms.d.ts +63 -0
  93. package/dist/lib/remote-ui/components/forms.d.ts.map +1 -0
  94. package/dist/lib/remote-ui/components/forms.js +117 -0
  95. package/dist/lib/remote-ui/components/forms.js.map +1 -0
  96. package/dist/lib/remote-ui/components/layout.d.ts +25 -0
  97. package/dist/lib/remote-ui/components/layout.d.ts.map +1 -0
  98. package/dist/lib/remote-ui/components/layout.js +66 -0
  99. package/dist/lib/remote-ui/components/layout.js.map +1 -0
  100. package/dist/lib/remote-ui/contract-import.d.ts +82 -0
  101. package/dist/lib/remote-ui/contract-import.d.ts.map +1 -0
  102. package/dist/lib/remote-ui/contract-import.js +102 -0
  103. package/dist/lib/remote-ui/contract-import.js.map +1 -0
  104. package/dist/lib/remote-ui/contracts.d.ts +257 -0
  105. package/dist/lib/remote-ui/contracts.d.ts.map +1 -0
  106. package/dist/lib/remote-ui/contracts.js +12 -0
  107. package/dist/lib/remote-ui/contracts.js.map +1 -0
  108. package/dist/lib/remote-ui/customer-contract-contracts.d.ts +227 -0
  109. package/dist/lib/remote-ui/customer-contract-contracts.d.ts.map +1 -0
  110. package/dist/lib/remote-ui/customer-contract-contracts.js +10 -0
  111. package/dist/lib/remote-ui/customer-contract-contracts.js.map +1 -0
  112. package/dist/lib/remote-ui/model.d.ts +31 -0
  113. package/dist/lib/remote-ui/model.d.ts.map +1 -0
  114. package/dist/lib/remote-ui/model.js +248 -0
  115. package/dist/lib/remote-ui/model.js.map +1 -0
  116. package/dist/lib/remote-ui/pages/analytics.d.ts +12 -0
  117. package/dist/lib/remote-ui/pages/analytics.d.ts.map +1 -0
  118. package/dist/lib/remote-ui/pages/analytics.js +123 -0
  119. package/dist/lib/remote-ui/pages/analytics.js.map +1 -0
  120. package/dist/lib/remote-ui/pages/catalogs.d.ts +225 -0
  121. package/dist/lib/remote-ui/pages/catalogs.d.ts.map +1 -0
  122. package/dist/lib/remote-ui/pages/catalogs.js +572 -0
  123. package/dist/lib/remote-ui/pages/catalogs.js.map +1 -0
  124. package/dist/lib/remote-ui/pages/contracts.d.ts +80 -0
  125. package/dist/lib/remote-ui/pages/contracts.d.ts.map +1 -0
  126. package/dist/lib/remote-ui/pages/contracts.js +396 -0
  127. package/dist/lib/remote-ui/pages/contracts.js.map +1 -0
  128. package/dist/lib/remote-ui/pages/files.d.ts +31 -0
  129. package/dist/lib/remote-ui/pages/files.d.ts.map +1 -0
  130. package/dist/lib/remote-ui/pages/files.js +64 -0
  131. package/dist/lib/remote-ui/pages/files.js.map +1 -0
  132. package/dist/lib/remote-ui/pages/home.d.ts +71 -0
  133. package/dist/lib/remote-ui/pages/home.d.ts.map +1 -0
  134. package/dist/lib/remote-ui/pages/home.js +136 -0
  135. package/dist/lib/remote-ui/pages/home.js.map +1 -0
  136. package/dist/lib/remote-ui/pages/procurement.d.ts +36 -0
  137. package/dist/lib/remote-ui/pages/procurement.d.ts.map +1 -0
  138. package/dist/lib/remote-ui/pages/procurement.js +370 -0
  139. package/dist/lib/remote-ui/pages/procurement.js.map +1 -0
  140. package/dist/lib/remote-ui/pages/sales.d.ts +12 -0
  141. package/dist/lib/remote-ui/pages/sales.d.ts.map +1 -0
  142. package/dist/lib/remote-ui/pages/sales.js +106 -0
  143. package/dist/lib/remote-ui/pages/sales.js.map +1 -0
  144. package/dist/lib/remote-ui/pages/settings.d.ts +34 -0
  145. package/dist/lib/remote-ui/pages/settings.d.ts.map +1 -0
  146. package/dist/lib/remote-ui/pages/settings.js +259 -0
  147. package/dist/lib/remote-ui/pages/settings.js.map +1 -0
  148. package/dist/lib/remote-ui/procurement-contracts.d.ts +177 -0
  149. package/dist/lib/remote-ui/procurement-contracts.d.ts.map +1 -0
  150. package/dist/lib/remote-ui/procurement-contracts.js +2 -0
  151. package/dist/lib/remote-ui/procurement-contracts.js.map +1 -0
  152. package/dist/lib/remote-ui/sales-contracts.d.ts +133 -0
  153. package/dist/lib/remote-ui/sales-contracts.d.ts.map +1 -0
  154. package/dist/lib/remote-ui/sales-contracts.js +2 -0
  155. package/dist/lib/remote-ui/sales-contracts.js.map +1 -0
  156. package/dist/lib/remote-ui/settings-contracts.d.ts +114 -0
  157. package/dist/lib/remote-ui/settings-contracts.d.ts.map +1 -0
  158. package/dist/lib/remote-ui/settings-contracts.js +6 -0
  159. package/dist/lib/remote-ui/settings-contracts.js.map +1 -0
  160. package/dist/lib/remote-ui/upload-policy.d.ts +18 -0
  161. package/dist/lib/remote-ui/upload-policy.d.ts.map +1 -0
  162. package/dist/lib/remote-ui/upload-policy.js +56 -0
  163. package/dist/lib/remote-ui/upload-policy.js.map +1 -0
  164. package/dist/lib/runtime-validation.d.ts +61 -0
  165. package/dist/lib/runtime-validation.d.ts.map +1 -0
  166. package/dist/lib/runtime-validation.js +243 -0
  167. package/dist/lib/runtime-validation.js.map +1 -0
  168. package/dist/lib/sales-file.service.d.ts +54 -6
  169. package/dist/lib/sales-file.service.d.ts.map +1 -1
  170. package/dist/lib/sales-file.service.js +278 -33
  171. package/dist/lib/sales-file.service.js.map +1 -1
  172. package/dist/lib/trade-compliance.config.d.ts +32 -0
  173. package/dist/lib/trade-compliance.config.d.ts.map +1 -1
  174. package/dist/lib/trade-compliance.config.js +16 -4
  175. package/dist/lib/trade-compliance.config.js.map +1 -1
  176. package/dist/lib/types.d.ts +253 -1
  177. package/dist/lib/types.d.ts.map +1 -1
  178. package/dist/lib/types.js +24 -0
  179. package/dist/lib/types.js.map +1 -1
  180. package/dist/lib/workbench-view.provider.d.ts +675 -38
  181. package/dist/lib/workbench-view.provider.d.ts.map +1 -1
  182. package/dist/lib/workbench-view.provider.js +418 -42
  183. package/dist/lib/workbench-view.provider.js.map +1 -1
  184. package/dist/lib/workbench.middleware.d.ts +70 -19
  185. package/dist/lib/workbench.middleware.d.ts.map +1 -1
  186. package/dist/lib/workbench.middleware.js +5 -1
  187. package/dist/lib/workbench.middleware.js.map +1 -1
  188. package/dist/lib/workbench.plugin.d.ts.map +1 -1
  189. package/dist/lib/workbench.plugin.js +6 -4
  190. package/dist/lib/workbench.plugin.js.map +1 -1
  191. package/dist/lib/workbench.service.d.ts +629 -48
  192. package/dist/lib/workbench.service.d.ts.map +1 -1
  193. package/dist/lib/workbench.service.js +1818 -300
  194. package/dist/lib/workbench.service.js.map +1 -1
  195. package/package.json +15 -4
  196. package/scripts/seed-demo-data.mjs +138 -0
@@ -1 +1 @@
1
- {"version":3,"file":"workbench-view.provider.d.ts","sourceRoot":"","sources":["../../src/lib/workbench-view.provider.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,+BAA+B,EAAE,MAAM,wBAAwB,CAAA;AACxE,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AAC1D,OAAO,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAA;AASvE,qBAEa,oCAAoC;IACnC,OAAO,CAAC,QAAQ,CAAC,OAAO;IAAmC,OAAO,CAAC,QAAQ,CAAC,UAAU;IAAoB,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAjH,OAAO,EAAE,+BAA+B,EAAmB,UAAU,EAAE,gBAAgB,EAAmB,OAAO,EAAE,0BAA0B;IAE1K,QAAQ,CAAC,OAAO,EAAE,GAAG;IAErB,gBAAgB,CAAC,QAAQ,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAatC,uBAAuB,CAAC,QAAQ,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG;;;;IAYtE,WAAW,CAAC,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAQtD,iBAAiB,CAAC,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAoCjF,qBAAqB,CAAC,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkBtG"}
1
+ {"version":3,"file":"workbench-view.provider.d.ts","sourceRoot":"","sources":["../../src/lib/workbench-view.provider.ts"],"names":[],"mappings":"AAKA,OAAO,EAAoG,KAAK,qBAAqB,EAAE,MAAM,sBAAsB,CAAA;AACnK,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AAGrD,OAAO,EAAE,+BAA+B,EAAqC,MAAM,wBAAwB,CAAA;AAC3G,OAAO,EAA6B,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AACrF,OAAO,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAA;AAGvE,OAAO,EAAwO,KAAK,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAExR,OAAO,EAAE,8BAA8B,EAA0B,MAAM,wBAAwB,CAAA;AAO/F,qBAEa,oCAAoC;IAE7C,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,OAAO;IACZ,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC;IACY,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC;gBAJjE,OAAO,EAAE,+BAA+B,EACxC,UAAU,EAAE,gBAAgB,EAC5B,OAAO,EAAE,0BAA0B,EACvB,QAAQ,CAAC,EAAE,8BAA8B,YAAA,EACH,cAAc,CAAC,EAAE,qBAAqB,YAAA;IAG3G,QAAQ,CAAC,OAAO,EAAE,GAAG;IAErB,gBAAgB,CAAC,QAAQ,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAatC,uBAAuB,CAAC,QAAQ,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG;;;;IAUtE,WAAW,CAAC,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAsDtD,iBAAiB,CAAC,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAqGjF,qBAAqB,CAAC,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;YAoCvF,iBAAiB;IAsB/B,OAAO,CAAC,QAAQ;CAGjB;AAoDD,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,gBAAgB,CASvG;AAsHD,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,UAA0E"}
@@ -1,25 +1,32 @@
1
- import { __decorate, __metadata } from "tslib";
2
- import { Injectable } from '@nestjs/common';
1
+ import { __decorate, __metadata, __param } from "tslib";
2
+ import { Inject, Injectable, Optional } from '@nestjs/common';
3
3
  import { createRequire } from 'node:module';
4
4
  import { dirname, join } from 'node:path';
5
5
  import { fileURLToPath } from 'node:url';
6
6
  import { readFile } from 'node:fs/promises';
7
- import { renderRemoteReactIframeHtml, ViewExtensionProvider } from '@xpert-ai/plugin-sdk';
7
+ import { PLUGIN_CONFIG_RESOLVER_TOKEN, renderRemoteReactIframeHtml, RequestContext, ViewExtensionProvider } from '@xpert-ai/plugin-sdk';
8
+ import { PermissionsEnum } from '@xpert-ai/contracts';
9
+ import XLSX from 'xlsx';
8
10
  import { FIXED_SLOT, FEATURE, ICON, MAIN_SLOT, PLUGIN_NAME, REMOTE_ENTRY_KEY, TOOL_NAMES, VIEW_KEY, VIEW_PROVIDER_KEY } from './constants.js';
9
11
  import { TradeComplianceWorkbenchService } from './workbench.service.js';
10
- import { SalesFileService } from './sales-file.service.js';
12
+ import { MAX_EDITED_WORKBOOK_BYTES, SalesFileService } from './sales-file.service.js';
11
13
  import { TradeComplianceFileStorage } from './adapters/file-storage.js';
12
14
  import { extractDocumentText } from './adapters/document-text-extractor.js';
13
15
  import { getHsBianmaCodeDetail, searchHsBianmaCodes } from './adapters/hsbianma.provider.js';
14
- import { DomainError, DomainErrorCodes, SourceFileFormats } from './types.js';
16
+ import { DomainError, DomainErrorCodes, SalesFileTypes, SourceFileFormats } from './types.js';
17
+ import { parseAnalyticsQuery, parseBankAccountType, parseExpectedRevision, parseRestoreSettingsInput, parseSettingsType } from './runtime-validation.js';
18
+ import { TradeComplianceDemoDataService } from './demo-data.service.js';
19
+ import { resolveTradeComplianceConfig } from './trade-compliance.config.js';
15
20
  const moduleDir = dirname(fileURLToPath(import.meta.url));
16
21
  const requireFromHere = createRequire(import.meta.url);
17
22
  const text = (en_US, zh_Hans) => ({ en_US, zh_Hans });
18
23
  let TradeComplianceWorkbenchViewProvider = class TradeComplianceWorkbenchViewProvider {
19
- constructor(service, salesFiles, storage) {
24
+ constructor(service, salesFiles, storage, demoData, configResolver) {
20
25
  this.service = service;
21
26
  this.salesFiles = salesFiles;
22
27
  this.storage = storage;
28
+ this.demoData = demoData;
29
+ this.configResolver = configResolver;
23
30
  }
24
31
  supports(context) { return context.hostType === 'agent'; }
25
32
  getViewManifests(_context, slot) {
@@ -38,22 +45,78 @@ let TradeComplianceWorkbenchViewProvider = class TradeComplianceWorkbenchViewPro
38
45
  async getRemoteComponentEntry(_context, viewKey, component) {
39
46
  if (viewKey !== VIEW_KEY || component.entry !== REMOTE_ENTRY_KEY)
40
47
  return { html: '<!doctype html><html><body>Unsupported component.</body></html>', contentType: 'text/html; charset=utf-8' };
41
- const [appScript, editorScript, editorCss, reactUmd, reactDomUmd] = await Promise.all([
48
+ const [appScript, appCss, reactUmd, reactDomUmd] = await Promise.all([
42
49
  readFile(join(moduleDir, 'remote-components', REMOTE_ENTRY_KEY, 'app.js'), 'utf8'),
43
- readFile(join(moduleDir, 'remote-components', REMOTE_ENTRY_KEY, 'editor.js'), 'utf8'),
44
- readFile(join(moduleDir, 'remote-components', REMOTE_ENTRY_KEY, 'editor.css'), 'utf8'),
50
+ readFile(join(moduleDir, 'remote-components', REMOTE_ENTRY_KEY, 'app.css'), 'utf8'),
45
51
  readPackageFile('react', 'umd/react.production.min.js'), readPackageFile('react-dom', 'umd/react-dom.production.min.js')
46
52
  ]);
47
- const spreadsheetRuntime = `(()=>{const style=document.createElement('style');style.textContent=${JSON.stringify(editorCss)};document.head.appendChild(style)})();\n${editorScript}\n${appScript}`;
48
- return { html: renderRemoteReactIframeHtml({ title: '外贸合规工作台', lang: 'zh-Hans', reactUmd, reactDomUmd, appScript: spreadsheetRuntime }), contentType: 'text/html; charset=utf-8' };
53
+ return { html: renderRemoteReactIframeHtml({ title: '外贸合规工作台', lang: 'zh-Hans', reactUmd, reactDomUmd, appScript, appCss: sanitizeIframeCss(appCss) }), contentType: 'text/html; charset=utf-8' };
49
54
  }
50
55
  async getViewData(context, _viewKey, query) {
51
56
  const scope = scopeFromContext(context);
52
- const all = { page: 1, pageSize: 200, search: query?.search, parameters: query?.parameters ?? {} };
53
- const [dashboard, controlledGoods, controlledBatches, sanctionedCompanies, sanctionBatches, suppliers, purchaseOrders, purchaseLines, customers, customerContracts, derivedSalesOrders, generatedFiles, settings, analytics, importTasks] = await Promise.all([
54
- this.service.dashboard(scope), this.service.listControlledGoods(scope, all), this.service.listControlledBatches(scope, all), this.service.listSanctionedCompanies(scope, all), this.service.listSanctionBatches(scope, all), this.service.listSuppliers(scope, all), this.service.listPurchaseOrders(scope, undefined, all), this.service.listPurchaseLines(scope, undefined, all), this.service.listCustomers(scope, all), this.service.listCustomerContracts(scope, all), this.service.listDerivedSalesOrders(scope), this.service.listGeneratedFiles(scope, all), this.service.settings(scope), this.service.analytics(scope, { ...(query?.parameters ?? {}), search: query?.search }), this.service.listImportTasks(scope, all)
57
+ const parameters = record(query?.parameters);
58
+ const collection = stringValue(parameters.collection);
59
+ if (collection) {
60
+ const pageQuery = { page: numberValue(query?.page, 1), pageSize: numberValue(query?.pageSize, 20), parameters: record(parameters.filters) };
61
+ if (collection === 'controlledGoods')
62
+ return { collection, ...await this.service.listControlledGoods(scope, pageQuery) };
63
+ if (collection === 'sanctionedCompanies')
64
+ return { collection, ...await this.service.listSanctionedCompanies(scope, pageQuery) };
65
+ if (collection === 'suppliers')
66
+ return { collection, ...await this.service.listSuppliers(scope, pageQuery) };
67
+ if (collection === 'purchaseOrders') {
68
+ const supplierId = requiredString(parameters.supplierId, 'supplierId');
69
+ const [page, supplier, metrics] = await Promise.all([
70
+ this.service.listPurchaseOrders(scope, supplierId, pageQuery),
71
+ this.service.getSupplier(scope, supplierId),
72
+ this.service.getSupplierProcurementMetrics(scope, supplierId)
73
+ ]);
74
+ return { collection, ...page, supplier, metrics };
75
+ }
76
+ if (collection === 'purchaseLines') {
77
+ const purchaseOrderId = requiredString(parameters.purchaseOrderId, 'purchaseOrderId');
78
+ const [page, order] = await Promise.all([
79
+ this.service.listPurchaseLines(scope, purchaseOrderId, pageQuery),
80
+ this.service.getPurchaseOrder(scope, purchaseOrderId)
81
+ ]);
82
+ return { collection, ...page, order, supplier: await this.service.getSupplier(scope, order.supplierId) };
83
+ }
84
+ if (collection === 'customerContracts')
85
+ return { collection, ...await this.service.listCustomerContracts(scope, pageQuery) };
86
+ if (collection === 'salesCustomers')
87
+ return { collection, ...await this.service.listSalesCustomers(scope, pageQuery) };
88
+ if (collection === 'salesOrders')
89
+ return { collection, ...await this.service.listSalesOrders(scope, requiredString(parameters.customerId, 'customerId'), pageQuery) };
90
+ if (collection === 'salesLines') {
91
+ const [customerContractId, purchaseOrderId] = salesOrderKey(requiredString(parameters.orderKey, 'orderKey'));
92
+ return { collection, ...await this.service.listSalesLines(scope, { customerId: requiredString(parameters.customerId, 'customerId'), customerContractId, purchaseOrderId, ...pageQuery }) };
93
+ }
94
+ if (collection === 'salesFileOrders')
95
+ return { collection, ...await this.service.listSalesFileOrders(scope, pageQuery) };
96
+ if (collection === 'generatedFiles')
97
+ return { collection, ...await this.service.listGeneratedFiles(scope, { ...pageQuery, parameters: { ...pageQuery.parameters, customerContractId: parameters.customerContractId, purchaseOrderId: parameters.purchaseOrderId } }) };
98
+ if (collection === 'analytics')
99
+ return { collection, ...await this.service.analytics(scope, parseAnalyticsQuery({ mode: parameters.mode, dimension: parameters.dimension, filters: record(parameters.filters), page: query?.page, pageSize: query?.pageSize })) };
100
+ if (collection === 'settings')
101
+ return { collection, ...await this.service.settings(scope) };
102
+ if (collection === 'customerContractAnalysis')
103
+ return { collection, ...await this.service.getCustomerContractReanalysisResult(scope, requiredString(parameters.runId, 'runId')) };
104
+ if (collection === 'importTask')
105
+ return { collection, ...await this.service.getImportTaskResult(scope, requiredString(parameters.taskId, 'taskId')) };
106
+ if (collection === 'importIssues')
107
+ return { collection, ...await this.service.listImportIssues(scope, {
108
+ documentType: catalogDocumentType(parameters.documentType), batchId: requiredString(parameters.batchId, 'batchId'), issueType: importIssueType(parameters.issueType), page: pageQuery.page, pageSize: pageQuery.pageSize
109
+ }) };
110
+ throw new DomainError(DomainErrorCodes.INVALID_INPUT, '不支持的数据集合', { collection });
111
+ }
112
+ const all = { page: 1, pageSize: 200, search: query?.search, parameters };
113
+ const catalogPage = { page: 1, pageSize: 20, parameters: {} };
114
+ const [dashboard, controlledGoods, controlledBatches, sanctionedCompanies, sanctionBatches, customers, derivedSalesOrders, generatedFiles, settings, analytics, importTasks] = await Promise.all([
115
+ this.service.dashboard(scope), this.service.listControlledGoods(scope, catalogPage), this.service.listControlledBatches(scope, all), this.service.listSanctionedCompanies(scope, catalogPage), this.service.listSanctionBatches(scope, all), this.service.listCustomers(scope, all), this.service.listDerivedSalesOrders(scope), this.service.listGeneratedFiles(scope, all), this.service.settings(scope), this.service.analytics(scope, { mode: 'PURCHASE', dimension: 'SUPPLIER', filters: {}, page: 1, pageSize: 20 }), this.service.listImportTasks(scope, all)
55
116
  ]);
56
- return { items: [], total: 0, summary: { dashboard, controlledGoods, controlledBatches, sanctionedCompanies, sanctionBatches, suppliers, purchaseOrders, purchaseLines, customers, customerContracts, derivedSalesOrders, generatedFiles, settings, analytics, importTasks } };
117
+ const emptyCollection = { items: [], total: 0, page: 1, pageSize: 20 };
118
+ const uploadPolicy = { maxUploadMb: resolveTradeComplianceConfig(this.configResolver, scope.organizationId).storage.maxUploadMb };
119
+ return { items: [], total: 0, summary: { uploadPolicy, dashboard, controlledGoods, controlledBatches, sanctionedCompanies, sanctionBatches, suppliers: emptyCollection, purchaseOrders: emptyCollection, purchaseLines: emptyCollection, customers, customerContracts: emptyCollection, derivedSalesOrders, generatedFiles, settings, analytics, importTasks } };
57
120
  }
58
121
  async executeViewAction(context, _viewKey, actionKey, request) {
59
122
  const scope = scopeFromContext(context);
@@ -62,6 +125,22 @@ let TradeComplianceWorkbenchViewProvider = class TradeComplianceWorkbenchViewPro
62
125
  let data;
63
126
  switch (actionKey) {
64
127
  case 'refresh': return { success: true, refresh: true, message: text('Refreshed', '已刷新') };
128
+ case 'load_excel_editor_runtime': {
129
+ const [script, editorCss] = await Promise.all([
130
+ readFile(join(moduleDir, 'remote-components', REMOTE_ENTRY_KEY, 'editor.js'), 'utf8'),
131
+ readFile(join(moduleDir, 'remote-components', REMOTE_ENTRY_KEY, 'editor.css'), 'utf8')
132
+ ]);
133
+ data = { script, css: sanitizeIframeCss(editorCss) };
134
+ break;
135
+ }
136
+ case 'seed_demo_data': {
137
+ RequestContext.hasPermission(PermissionsEnum.ORG_DEMO_EDIT, true);
138
+ parseDemoSeedInput(input);
139
+ if (!this.demoData)
140
+ throw new DomainError(DomainErrorCodes.INVALID_INPUT, '演示数据服务不可用');
141
+ data = await this.demoData.seed(scope);
142
+ break;
143
+ }
65
144
  case 'save_controlled_goods':
66
145
  data = await this.service.saveControlledGoods(scope, input);
67
146
  break;
@@ -104,21 +183,64 @@ let TradeComplianceWorkbenchViewProvider = class TradeComplianceWorkbenchViewPro
104
183
  case 'get_customer_contract_review':
105
184
  data = await this.service.getCustomerContractReview(scope, requiredString(input.id, 'id'));
106
185
  break;
107
- case 'update_customer_contract_review':
108
- data = await this.service.updateCustomerContractReview(scope, requiredString(input.id, 'id'), input);
186
+ case 'update_customer_contract_review': {
187
+ const parsed = customerContractReviewUpdate(request?.input);
188
+ data = await this.service.updateCustomerContractReview(scope, parsed.id, parsed.update);
189
+ break;
190
+ }
191
+ case 'reanalyze_customer_contract': {
192
+ const started = await this.service.startCustomerContractReanalysis(scope, { reviewSource: customerContractReviewSource(input.reviewSource), id: stringValue(input.id), importTaskId: stringValue(input.importTaskId) });
193
+ const proposedCommand = started.run.executionCommand ?? buildCustomerContractReanalysisCommand(started);
194
+ const run = await this.service.saveCustomerContractReanalysisExecution(scope, started.run.id, proposedCommand);
195
+ if (!run.executionCommand)
196
+ throw new DomainError(DomainErrorCodes.INVALID_INPUT, '重新分析任务缺少已持久化的执行命令');
197
+ data = { run, commands: [run.executionCommand], polling: { intervalMs: 2000, timeoutMs: 1200000 } };
198
+ break;
199
+ }
200
+ case 'mark_customer_contract_reanalysis_dispatched':
201
+ data = await this.service.markCustomerContractReanalysisDispatched(scope, requiredString(input.runId, 'runId'));
202
+ break;
203
+ case 'mark_customer_contract_reanalysis_failed':
204
+ data = await this.service.markCustomerContractReanalysisFailed(scope, requiredString(input.runId, 'runId'), { errorCode: requiredString(input.errorCode, 'errorCode'), errorMessage: requiredString(input.errorMessage, 'errorMessage') });
109
205
  break;
110
206
  case 'delete_entity':
111
207
  data = await this.service.softDelete(scope, requiredString(input.entityType, 'entityType'), requiredString(input.id, 'id'));
112
208
  break;
113
- case 'save_settings':
114
- data = await this.service.saveSettings(scope, input.settingsType, record(input.values));
209
+ case 'save_settings': {
210
+ const settingsType = parseSettingsType(input.settingsType);
211
+ data = await this.service.saveSettings(scope, settingsType, record(input.values), parseExpectedRevision(input.expectedRevision, settingsType === 'rate'));
212
+ break;
213
+ }
214
+ case 'restore_settings_default':
215
+ data = await this.service.restoreSettingsDefault(scope, parseRestoreSettingsInput(input));
216
+ break;
217
+ case 'delete_settings_rate': {
218
+ const settingsType = parseSettingsType(input.settingsType);
219
+ if (settingsType !== 'rate')
220
+ throw new DomainError(DomainErrorCodes.INVALID_INPUT, '删除月度汇率必须使用 rate 设置类型', { settingsType });
221
+ data = await this.service.deleteMonthlyRate(scope, { id: requiredString(input.id, 'id'), expectedRevision: parseExpectedRevision(input.expectedRevision) });
222
+ break;
223
+ }
224
+ case 'discard_company_logo': {
225
+ const logoFileId = requiredString(input.logoFileId, 'logoFileId');
226
+ await this.service.discardCompanyLogoDraft(scope, logoFileId);
227
+ data = { logoFileId };
228
+ break;
229
+ }
230
+ case 'export_analytics': {
231
+ const result = await this.service.exportAnalytics(scope, parseAnalyticsQuery({ mode: input.mode, dimension: input.dimension, filters: input.filters, page: 1, pageSize: 20 }));
232
+ data = { fileName: result.fileName, mimeType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', base64: result.buffer.toString('base64') };
233
+ break;
234
+ }
235
+ case 'preflight_sales_file':
236
+ data = await this.salesFiles.preflight(scope, salesFileGenerationInput(input));
115
237
  break;
116
238
  case 'generate_sales_file':
117
- data = await this.salesFiles.generate(scope, input);
239
+ data = await this.salesFiles.generate(scope, salesFileGenerationInput(input));
118
240
  break;
119
241
  case 'download_sales_file': {
120
242
  const result = await this.salesFiles.download(scope, requiredString(input.id, 'id'));
121
- data = { fileName: result.item.fileName, mimeType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', base64: result.buffer.toString('base64') };
243
+ data = { fileName: result.item.fileName, storageFileId: result.item.storageFileId, editVersion: result.item.editVersion, mimeType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', base64: result.buffer.toString('base64') };
122
244
  break;
123
245
  }
124
246
  case 'download_import_issues': {
@@ -126,19 +248,39 @@ let TradeComplianceWorkbenchViewProvider = class TradeComplianceWorkbenchViewPro
126
248
  data = { fileName: result.fileName, mimeType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', base64: result.buffer.toString('base64') };
127
249
  break;
128
250
  }
251
+ case 'retry_import_task':
252
+ data = await this.startCatalogRetry(scope, requiredString(input.taskId, 'taskId'));
253
+ break;
254
+ case 'mark_import_command_dispatched':
255
+ data = await this.service.markImportCommandDispatched(scope, requiredString(input.taskId, 'taskId'));
256
+ break;
129
257
  case 'download_source_file': {
130
- const buffer = await this.storage.read(requiredString(input.sourceFileId, 'sourceFileId'));
131
- data = { fileName: requiredString(input.fileName, 'fileName'), mimeType: 'application/octet-stream', base64: buffer.toString('base64') };
258
+ const descriptor = await this.service.getSourceFileDescriptor(scope, requiredString(input.sourceFileId, 'sourceFileId'));
259
+ const buffer = await this.storage.read(descriptor.sourceFileId);
260
+ data = { fileName: descriptor.sourceFileName, mimeType: mimeTypeForFile(descriptor.sourceFileName), base64: buffer.toString('base64') };
132
261
  break;
133
262
  }
134
- case 'save_edited_sales_file':
135
- data = await this.salesFiles.saveEditedWorkbook(scope, requiredString(input.id, 'id'), Buffer.from(requiredString(input.base64, 'base64'), 'base64'));
263
+ case 'preview_source_file': {
264
+ const descriptor = await this.service.getSourceFileDescriptor(scope, requiredString(input.sourceFileId, 'sourceFileId'));
265
+ const buffer = await this.storage.read(descriptor.sourceFileId);
266
+ data = await sourcePreview(descriptor.sourceFileName, buffer);
136
267
  break;
268
+ }
269
+ case 'save_edited_sales_file': {
270
+ const base64 = requiredString(input.base64, 'base64');
271
+ if (base64.length > Math.ceil(MAX_EDITED_WORKBOOK_BYTES / 3) * 4)
272
+ throw new DomainError(DomainErrorCodes.FILE_TOO_LARGE, '编辑后的工作簿不能超过 20 MB');
273
+ const buffer = Buffer.from(base64, 'base64');
274
+ if (buffer.length > MAX_EDITED_WORKBOOK_BYTES)
275
+ throw new DomainError(DomainErrorCodes.FILE_TOO_LARGE, '编辑后的工作簿不能超过 20 MB');
276
+ data = await this.salesFiles.saveEditedWorkbook(scope, requiredString(input.id, 'id'), buffer, { editVersion: requiredNonNegativeInteger(input.editVersion, 'editVersion'), expectedStorageFileId: requiredString(input.expectedStorageFileId, 'expectedStorageFileId') });
277
+ break;
278
+ }
137
279
  case 'search_hs_code':
138
- data = await searchHsBianmaCodes({ keywords: requiredString(input.keywords, 'keywords'), page: numberValue(input.page, 1), filterFailureCode: true, displayChapter: false, displayEnName: true }, hsConfig());
280
+ data = await searchHsBianmaCodes({ keywords: requiredString(input.keywords, 'keywords'), page: numberValue(input.page, 1), filterFailureCode: true, displayChapter: false, displayEnName: true }, this.hsConfig(scope));
139
281
  break;
140
282
  case 'get_hs_code_detail':
141
- data = await getHsBianmaCodeDetail({ code: stringValue(input.code), detailUrl: stringValue(input.detailUrl) }, hsConfig());
283
+ data = { ...await getHsBianmaCodeDetail({ code: stringValue(input.code), detailUrl: stringValue(input.detailUrl) }, this.hsConfig(scope)), queriedAt: new Date().toISOString() };
142
284
  break;
143
285
  default: throw new DomainError(DomainErrorCodes.INVALID_INPUT, '不支持的操作', { actionKey });
144
286
  }
@@ -153,43 +295,96 @@ let TradeComplianceWorkbenchViewProvider = class TradeComplianceWorkbenchViewPro
153
295
  return failure(new DomainError(DomainErrorCodes.INVALID_INPUT, '不支持的视图'));
154
296
  const scope = scopeFromContext(context);
155
297
  const input = record(request?.input);
298
+ let task;
156
299
  try {
157
- const fileName = fileNameOf(file, input);
158
- const format = formatOf(fileName);
159
- const size = Number(file?.size ?? 0);
160
- if (!format)
161
- throw new DomainError(DomainErrorCodes.UNSUPPORTED_FILE_FORMAT, '仅支持 PDF、Word、Excel 和常见图片格式');
162
- if (size > 20 * 1024 * 1024)
163
- throw new DomainError(DomainErrorCodes.FILE_TOO_LARGE, '文件不能超过 20 MB');
164
- const buffer = bufferOf(file) ?? await bufferFromPath(input);
300
+ const buffer = bufferOf(file);
165
301
  if (!buffer?.length)
166
302
  throw new DomainError(DomainErrorCodes.INVALID_INPUT, '无法读取上传文件内容');
303
+ if (actionKey === 'upload_company_logo') {
304
+ if (buffer.length > 2 * 1024 * 1024)
305
+ throw new DomainError(DomainErrorCodes.FILE_TOO_LARGE, '公司 Logo 不能超过 2 MB');
306
+ const fileName = fileNameOf(file, input);
307
+ const format = validateSourceFile(fileName, String(file?.mimetype ?? file?.type ?? ''), buffer);
308
+ if (format !== 'PNG' && format !== 'JPEG')
309
+ throw new DomainError(DomainErrorCodes.UNSUPPORTED_FILE_FORMAT, '公司 Logo 仅支持 PNG、JPG/JPEG');
310
+ const asset = await this.service.createCompanyLogoDraft(scope, buffer, fileName);
311
+ return { success: true, refresh: false, message: text('Logo uploaded', '公司 Logo 上传成功'), data: { logoFileId: asset.id } };
312
+ }
313
+ const maxUploadMb = resolveTradeComplianceConfig(this.configResolver, scope.organizationId).storage.maxUploadMb;
314
+ if (buffer.length > maxUploadMb * 1024 * 1024)
315
+ throw new DomainError(DomainErrorCodes.FILE_TOO_LARGE, `文件不能超过 ${maxUploadMb} MB`);
316
+ const fileName = fileNameOf(file, input);
317
+ const format = validateSourceFile(fileName, String(file?.mimetype ?? file?.type ?? ''), buffer);
318
+ const sourcePlatformFileId = platformFileId(file, input);
319
+ if (!sourcePlatformFileId)
320
+ throw new DomainError(DomainErrorCodes.INVALID_INPUT, '上传文件缺少平台文件标识,请重新上传');
167
321
  const documentType = documentTypeForAction(actionKey);
168
322
  const storageFileId = await this.storage.save(buffer, fileName, 'uploads');
169
- const task = await this.service.createImportTask(scope, { documentType, sourceFileId: storageFileId, sourceFileName: fileName, sourceFileFormat: format, status: 'EXTRACTING' });
323
+ const executionInput = executionInputForAction(actionKey, input);
324
+ task = await this.service.createImportTask(scope, { documentType, sourceFileId: storageFileId, sourceFileName: fileName, sourceFileFormat: format, sourcePlatformFileId, executionInput, status: 'EXTRACTING' });
170
325
  let extracted = { text: '', chunks: [], kind: 'image', originalLength: 0 };
171
326
  if (!['PNG', 'JPEG', 'WEBP'].includes(format))
172
327
  extracted = await extractDocumentText({ buffer, fileName, mimeType: file?.mimetype ?? file?.type, maxChars: 100000 });
173
328
  const parsingTask = await this.service.markImportTaskParsing(scope, task.id);
174
329
  const command = buildAssistantCommand({ actionKey, file, input, task, extracted, catalogType: input.catalogType, replaceBatchId: input.replaceBatchId });
330
+ await this.service.saveImportTaskExecution(scope, task.id, command);
175
331
  return { success: true, refresh: false, message: text('Parsing started', '文件已进入解析中,请勿重复操作'), data: { task: parsingTask, commands: [command], polling: { intervalMs: 2000, timeoutMs: 1200000 } } };
176
332
  }
177
333
  catch (error) {
334
+ if (task?.id)
335
+ await this.service.markImportTaskFailed(scope, task.id, { errorCode: error instanceof DomainError ? error.code : DomainErrorCodes.INVALID_INPUT, errorMessage: error instanceof Error ? error.message : String(error) });
178
336
  return failure(error);
179
337
  }
180
338
  }
339
+ async startCatalogRetry(scope, sourceTaskId) {
340
+ let task;
341
+ try {
342
+ task = await this.service.createRetryImportTask(scope, sourceTaskId);
343
+ const buffer = await this.storage.read(task.sourceFileId);
344
+ let extracted = { text: '', chunks: [], kind: 'image', originalLength: 0 };
345
+ if (!['PNG', 'JPEG', 'WEBP'].includes(task.sourceFileFormat))
346
+ extracted = await extractDocumentText({ buffer, fileName: task.sourceFileName, maxChars: 100000 });
347
+ if (!task.sourcePlatformFileId)
348
+ throw new DomainError(DomainErrorCodes.INVALID_INPUT, '源文件缺少平台文件标识,无法重新解析');
349
+ const executionInput = task.executionInput;
350
+ if (!executionInput)
351
+ throw new DomainError(DomainErrorCodes.INVALID_INPUT, '重试任务缺少执行参数');
352
+ const file = { name: task.sourceFileName, size: buffer.length, fileAssetId: task.sourcePlatformFileId, fileId: task.sourcePlatformFileId };
353
+ const input = { ...executionInput, fileAssetId: task.sourcePlatformFileId };
354
+ const command = buildAssistantCommand({ actionKey: executionInput.actionKey, file, input, task, extracted, catalogType: executionInput.catalogType, replaceBatchId: executionInput.replaceBatchId });
355
+ const parsingTask = await this.service.markImportTaskParsing(scope, task.id);
356
+ await this.service.saveImportTaskExecution(scope, task.id, command);
357
+ return { task: parsingTask, commands: [command], polling: { intervalMs: 2000, timeoutMs: 1200000 } };
358
+ }
359
+ catch (error) {
360
+ if (task?.id)
361
+ await this.service.markImportTaskFailed(scope, task.id, { errorCode: error instanceof DomainError ? error.code : DomainErrorCodes.INVALID_INPUT, errorMessage: error instanceof Error ? error.message : String(error) });
362
+ throw error;
363
+ }
364
+ }
365
+ hsConfig(scope) {
366
+ return resolveTradeComplianceConfig(this.configResolver, scope.organizationId).hsBianma;
367
+ }
181
368
  };
182
369
  TradeComplianceWorkbenchViewProvider = __decorate([
183
370
  Injectable(),
184
371
  ViewExtensionProvider(VIEW_PROVIDER_KEY),
185
- __metadata("design:paramtypes", [TradeComplianceWorkbenchService, SalesFileService, TradeComplianceFileStorage])
372
+ __param(3, Optional()),
373
+ __param(4, Optional()),
374
+ __param(4, Inject(PLUGIN_CONFIG_RESOLVER_TOKEN)),
375
+ __metadata("design:paramtypes", [TradeComplianceWorkbenchService,
376
+ SalesFileService,
377
+ TradeComplianceFileStorage,
378
+ TradeComplianceDemoDataService, Object])
186
379
  ], TradeComplianceWorkbenchViewProvider);
187
380
  export { TradeComplianceWorkbenchViewProvider };
188
381
  function actionManifests() {
189
382
  const invoke = (key, label, icon = 'ri-edit-line') => ({ key, label: text(key, label), icon, actionType: 'invoke' });
190
383
  const upload = (key, label) => ({ ...invoke(key, label, 'ri-upload-cloud-line'), transport: 'file', placement: 'toolbar' });
191
- return [invoke('refresh', '刷新', 'ri-refresh-line'), upload('upload_controlled_catalog', '上传管控目录'), upload('upload_sanction_catalog', '上传制裁目录'), upload('upload_purchase_contract', '上传采购合同'), upload('upload_customer_contract', '上传客户合同'),
192
- ...['save_controlled_goods', 'update_controlled_goods', 'save_sanctioned_company', 'update_sanctioned_company', 'save_purchase_review', 'save_customer_contract_review', 'update_supplier', 'update_purchase_order', 'add_purchase_line', 'update_purchase_line', 'update_customer', 'save_customer_profit_rate', 'save_derived_manual_data', 'get_customer_contract_review', 'update_customer_contract_review', 'delete_entity', 'save_settings', 'generate_sales_file', 'download_sales_file', 'download_import_issues', 'download_source_file', 'save_edited_sales_file', 'search_hs_code', 'get_hs_code_detail'].map(key => invoke(key, key))];
384
+ const fileInvoke = (key, label) => ({ ...invoke(key, label, 'ri-upload-cloud-line'), transport: 'file' });
385
+ return [invoke('refresh', '刷新', 'ri-refresh-line'), upload('upload_controlled_catalog', '上传管控目录'), upload('upload_sanction_catalog', '上传制裁目录'), upload('upload_purchase_contract', '上传采购合同'), upload('upload_customer_contract', '上传客户合同'), fileInvoke('upload_company_logo', '上传公司 Logo'),
386
+ { ...invoke('seed_demo_data', 'seed_demo_data'), permissions: [PermissionsEnum.ORG_DEMO_EDIT] },
387
+ ...['load_excel_editor_runtime', 'save_controlled_goods', 'update_controlled_goods', 'save_sanctioned_company', 'update_sanctioned_company', 'retry_import_task', 'mark_import_command_dispatched', 'save_purchase_review', 'save_customer_contract_review', 'update_supplier', 'update_purchase_order', 'add_purchase_line', 'update_purchase_line', 'update_customer', 'save_customer_profit_rate', 'save_derived_manual_data', 'get_customer_contract_review', 'update_customer_contract_review', 'reanalyze_customer_contract', 'mark_customer_contract_reanalysis_dispatched', 'mark_customer_contract_reanalysis_failed', 'delete_entity', 'save_settings', 'restore_settings_default', 'delete_settings_rate', 'discard_company_logo', 'export_analytics', 'preflight_sales_file', 'generate_sales_file', 'download_sales_file', 'download_import_issues', 'download_source_file', 'preview_source_file', 'save_edited_sales_file', 'search_hs_code', 'get_hs_code_detail'].map(key => invoke(key, key))];
193
388
  }
194
389
  function buildAssistantCommand({ actionKey, file, input, task, extracted, catalogType, replaceBatchId }) {
195
390
  const toolName = actionKey === 'upload_controlled_catalog' ? TOOL_NAMES.saveControlledCatalog : actionKey === 'upload_sanction_catalog' ? TOOL_NAMES.saveSanctionCatalog : actionKey === 'upload_purchase_contract' ? TOOL_NAMES.savePurchaseDraft : TOOL_NAMES.saveCustomerContractDraft;
@@ -198,17 +393,179 @@ function buildAssistantCommand({ actionKey, file, input, task, extracted, catalo
198
393
  const fixed = JSON.stringify({ importTaskId: task.id, sourceFileId: task.sourceFileId, sourceFileName: task.sourceFileName, catalogType, replaceBatchId });
199
394
  return { commandKey: 'assistant.chat.send_message', payload: { text: `请解析上传的外贸合规文件。${requirements}\n解析完成后必须调用工具 ${toolName} 保存结果,以下固定参数必须原样传入:${fixed}\n统一使用中文说明,不要遗漏文件中的记录。${source}`, clientMessageId: `trade-compliance:${task.id}`, files: [assistantFile(file, input, task.sourceFileName)], followUpMode: 'queue', state: { tradeComplianceWorkbench: { actionKey, importTaskId: task.id } } } };
200
395
  }
201
- function assistantFile(file, input, fileName) { const workspace = record(input.workspaceFile); return { id: input.fileAssetId ?? input.fileId ?? file?.fileAssetId ?? file?.fileId, fileId: input.fileAssetId ?? input.fileId ?? file?.fileAssetId ?? file?.fileId, fileAssetId: input.fileAssetId ?? input.fileId ?? file?.fileAssetId ?? file?.fileId, workspacePath: workspace.workspacePath ?? input.workspacePath, filePath: workspace.filePath ?? input.filePath, name: fileName, originalName: fileName, mimeType: file?.mimetype ?? file?.type, size: file?.size, role: 'source_file' }; }
396
+ function buildCustomerContractReanalysisCommand(started) {
397
+ if (started.run.promptVersion !== 'CUSTOMER_CLAUSE_V1')
398
+ throw new DomainError(DomainErrorCodes.INVALID_INPUT, '不支持的客户合同重新分析提示版本');
399
+ const fixed = JSON.stringify({ analysisRunId: started.run.id });
400
+ const clauses = JSON.stringify(started.clauses.map(clause => ({ clauseType: clause.clauseType, extractionStatus: clause.extractionStatus, reviewedText: clause.reviewedText, sourceText: clause.sourceText, sourceLocation: clause.sourceLocation, sourceContent: clause.sourceContent })));
401
+ return { commandKey: 'assistant.chat.send_message', payload: { text: `请重新分析客户合同七类关键条款。必须按 clauseType 返回风险等级、中文风险提示和修改建议,并调用工具 ${TOOL_NAMES.saveCustomerContractReanalysis} 保存结果。固定参数必须原样传入:${fixed}\n待分析条款:${clauses}`, clientMessageId: `trade-compliance:customer-reanalysis:${started.run.id}`, followUpMode: 'queue', state: { tradeComplianceWorkbench: { actionKey: 'reanalyze_customer_contract', analysisRunId: started.run.id } } } };
402
+ }
403
+ function assistantFile(file, input, fileName) {
404
+ const fileAssetId = input.fileAssetId ?? input.fileId ?? file?.fileAssetId ?? file?.fileId;
405
+ const storageFileId = input.storageFileId ?? file?.storageFileId;
406
+ return { id: fileAssetId, fileId: fileAssetId, fileAssetId, ...(storageFileId ? { storageFileId } : {}), name: fileName, originalName: fileName, mimeType: file?.mimetype ?? file?.type, size: file?.size, role: 'source_file' };
407
+ }
408
+ function platformFileId(file, input) { const value = input.fileAssetId ?? input.fileId ?? file?.fileAssetId ?? file?.fileId; return value ? String(value) : null; }
409
+ function executionInputForAction(actionKey, input) {
410
+ const typedActionKey = documentActionKey(actionKey);
411
+ const catalogType = input.catalogType === 'DUAL_USE' || input.catalogType === 'OVERSEAS_SANCTION' ? input.catalogType : undefined;
412
+ if (typedActionKey === 'upload_controlled_catalog' && !catalogType)
413
+ throw new DomainError(DomainErrorCodes.INVALID_INPUT, '管控目录任务缺少目录类型');
414
+ return { actionKey: typedActionKey, ...(catalogType ? { catalogType } : {}), ...(input.replaceBatchId ? { replaceBatchId: String(input.replaceBatchId) } : {}) };
415
+ }
416
+ function documentActionKey(actionKey) { documentTypeForAction(actionKey); return actionKey; }
417
+ function catalogDocumentType(value) { if (value === 'CONTROLLED_CATALOG' || value === 'SANCTION_CATALOG')
418
+ return value; throw new DomainError(DomainErrorCodes.INVALID_INPUT, '不支持的目录文档类型'); }
419
+ function importIssueType(value) { if (value === 'DUPLICATE' || value === 'INVALID')
420
+ return value; throw new DomainError(DomainErrorCodes.INVALID_INPUT, '不支持的导入问题类型'); }
421
+ function customerContractReviewSource(value) { if (value === 'IMPORT_DRAFT' || value === 'EXISTING_CONTRACT')
422
+ return value; throw new DomainError(DomainErrorCodes.INVALID_INPUT, '不支持的客户合同审核来源'); }
423
+ function salesOrderKey(value) { const parts = value.split(':'); if (parts.length !== 2 || parts.some(part => !part))
424
+ throw new DomainError(DomainErrorCodes.INVALID_INPUT, '销售订单标识无效'); return parts; }
425
+ function salesFileGenerationInput(input) {
426
+ if (!SalesFileTypes.includes(input.fileType))
427
+ throw new DomainError(DomainErrorCodes.INVALID_INPUT, '销售文件类型无效');
428
+ const fileType = input.fileType;
429
+ const bankType = input.bankType == null ? undefined : parseBankAccountType(input.bankType);
430
+ return { customerContractId: requiredString(input.customerContractId, 'customerContractId'), purchaseOrderId: requiredString(input.purchaseOrderId, 'purchaseOrderId'), fileType, ...(bankType ? { bankType } : {}) };
431
+ }
432
+ function requiredNonNegativeInteger(value, field) { const parsed = Number(value); if (!Number.isInteger(parsed) || parsed < 0)
433
+ throw new DomainError(DomainErrorCodes.INVALID_INPUT, `${field} 必须是非负整数`); return parsed; }
202
434
  function documentTypeForAction(action) { const map = { upload_controlled_catalog: 'CONTROLLED_CATALOG', upload_sanction_catalog: 'SANCTION_CATALOG', upload_purchase_contract: 'PURCHASE_CONTRACT', upload_customer_contract: 'CUSTOMER_CONTRACT' }; const value = map[action]; if (!value)
203
435
  throw new DomainError(DomainErrorCodes.INVALID_INPUT, '不支持的上传操作', { action }); return value; }
204
436
  function formatOf(fileName) { const extension = fileName.split('.').pop()?.toUpperCase(); const normalized = extension === 'JPG' ? 'JPEG' : extension; return SourceFileFormats.includes(normalized) ? normalized : null; }
437
+ export function validateSourceFile(fileName, mimeType, buffer) {
438
+ const extensionFormat = formatOf(fileName);
439
+ const mimeFormat = formatForMimeType(mimeType);
440
+ const signatureFormat = formatForSignature(buffer);
441
+ if (!extensionFormat)
442
+ throw new DomainError(DomainErrorCodes.UNSUPPORTED_FILE_FORMAT, '仅支持 PDF、Word、Excel 和常见图片格式');
443
+ if (extensionFormat !== mimeFormat || extensionFormat !== signatureFormat) {
444
+ throw new DomainError(DomainErrorCodes.UNSUPPORTED_FILE_FORMAT, '文件扩展名、MIME 类型与文件签名不一致');
445
+ }
446
+ return extensionFormat;
447
+ }
448
+ function formatForMimeType(mimeType) {
449
+ const formats = {
450
+ 'application/pdf': 'PDF',
451
+ 'application/msword': 'DOC',
452
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': 'DOCX',
453
+ 'application/vnd.ms-excel': 'XLS',
454
+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': 'XLSX',
455
+ 'image/png': 'PNG',
456
+ 'image/jpeg': 'JPEG',
457
+ 'image/webp': 'WEBP'
458
+ };
459
+ return formats[mimeType.trim().toLowerCase()] ?? null;
460
+ }
461
+ function formatForSignature(buffer) {
462
+ if (buffer.subarray(0, 5).equals(Buffer.from('%PDF-')))
463
+ return 'PDF';
464
+ if (buffer.subarray(0, 8).equals(Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a])))
465
+ return 'PNG';
466
+ if (buffer.length >= 3 && buffer[0] === 0xff && buffer[1] === 0xd8 && buffer[2] === 0xff)
467
+ return 'JPEG';
468
+ if (buffer.length >= 12 && buffer.subarray(0, 4).toString('ascii') === 'RIFF' && buffer.subarray(8, 12).toString('ascii') === 'WEBP')
469
+ return 'WEBP';
470
+ if (buffer.subarray(0, 8).equals(Buffer.from([0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1]))) {
471
+ let names;
472
+ try {
473
+ const container = XLSX.CFB.read(buffer, { type: 'buffer' });
474
+ names = container.FileIndex.filter((entry) => entry.type === 2).map((entry) => String(entry.name));
475
+ }
476
+ catch {
477
+ return null;
478
+ }
479
+ const isWord = names.includes('WordDocument');
480
+ const isExcel = names.includes('Workbook') || names.includes('Book');
481
+ if (isWord !== isExcel)
482
+ return isWord ? 'DOC' : 'XLS';
483
+ return null;
484
+ }
485
+ if (buffer.length >= 4 && [[0x03, 0x04], [0x05, 0x06], [0x07, 0x08]].some(([third, fourth]) => buffer[0] === 0x50 && buffer[1] === 0x4b && buffer[2] === third && buffer[3] === fourth)) {
486
+ let container;
487
+ try {
488
+ container = XLSX.CFB.read(buffer, { type: 'buffer' });
489
+ }
490
+ catch {
491
+ return null;
492
+ }
493
+ const entries = container.FileIndex
494
+ .map((entry, index) => entry.type === 2 ? packageEntryName(container.FullPaths[index]) : null)
495
+ .filter((name) => Boolean(name));
496
+ if (!entries.includes('[Content_Types].xml'))
497
+ return null;
498
+ const isWord = entries.some(name => name.startsWith('word/'));
499
+ const isExcel = entries.some(name => name.startsWith('xl/'));
500
+ if (isWord !== isExcel)
501
+ return isWord ? 'DOCX' : 'XLSX';
502
+ }
503
+ return null;
504
+ }
505
+ function packageEntryName(fullPath) { const value = String(fullPath ?? '').replace(/\\/g, '/'); const rootSeparator = value.indexOf('/'); return rootSeparator < 0 ? value : value.slice(rootSeparator + 1); }
506
+ function mimeTypeForFile(fileName) { const format = formatOf(fileName); return format === 'PDF' ? 'application/pdf' : format === 'PNG' ? 'image/png' : format === 'JPEG' ? 'image/jpeg' : format === 'WEBP' ? 'image/webp' : format === 'DOC' ? 'application/msword' : format === 'DOCX' ? 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' : format === 'XLS' ? 'application/vnd.ms-excel' : format === 'XLSX' ? 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' : 'application/octet-stream'; }
205
507
  function scopeFromContext(context) { if (!context.tenantId || !context.organizationId)
206
508
  throw new DomainError(DomainErrorCodes.INVALID_INPUT, '请求上下文缺少租户或组织标识'); return { tenantId: context.tenantId, organizationId: context.organizationId, userId: context.userId }; }
509
+ function parseDemoSeedInput(input) {
510
+ if (Object.keys(input).length !== 1 || input.confirmation !== 'INIT_DEMO_DATA') {
511
+ throw new DomainError(DomainErrorCodes.INVALID_INPUT, '初始化演示数据必须提供精确确认值 INIT_DEMO_DATA');
512
+ }
513
+ return { confirmation: input.confirmation };
514
+ }
207
515
  function success(data) { return { success: true, message: text('Saved', '已保存'), data, refresh: true }; }
208
516
  function failure(error) { const domain = error instanceof DomainError ? error : new DomainError(DomainErrorCodes.INVALID_INPUT, error instanceof Error ? error.message : String(error)); return { success: false, message: text(domain.message, domain.message), error: { code: domain.code, message: domain.message, details: domain.details }, refresh: false }; }
209
517
  function record(value) { return value && typeof value === 'object' && !Array.isArray(value) ? value : {}; }
210
518
  function requiredString(value, field) { const result = String(value ?? '').trim(); if (!result)
211
519
  throw new DomainError(DomainErrorCodes.INVALID_INPUT, `缺少 ${field}`); return result; }
520
+ function customerContractReviewUpdate(value) {
521
+ const payload = objectInput(value, '客户合同审核载荷');
522
+ const contractInput = objectInput(payload.contract, '合同信息');
523
+ const contract = {
524
+ contractNo: typedRequiredString(contractInput.contractNo, '客户合同编码')
525
+ };
526
+ if ('signedAt' in contractInput)
527
+ contract.signedAt = nullableString(contractInput.signedAt, '签订日期');
528
+ if ('contractAmountReviewCny' in contractInput)
529
+ contract.contractAmountReviewCny = nullableDecimalString(contractInput.contractAmountReviewCny, '人民币预审合同金额');
530
+ const update = { contract };
531
+ if ('customer' in payload)
532
+ update.customer = objectInput(payload.customer, '客户信息');
533
+ if ('clauses' in payload) {
534
+ if (!Array.isArray(payload.clauses))
535
+ throw new DomainError(DomainErrorCodes.INVALID_INPUT, '合同条款必须是数组');
536
+ update.clauses = payload.clauses;
537
+ }
538
+ if ('customerTransferConfirmation' in payload)
539
+ update.customerTransferConfirmation = objectInput(payload.customerTransferConfirmation, '客户转移确认');
540
+ return { id: typedRequiredString(payload.id, '客户合同标识'), update };
541
+ }
542
+ function objectInput(value, label) {
543
+ if (!value || typeof value !== 'object' || Array.isArray(value))
544
+ throw new DomainError(DomainErrorCodes.INVALID_INPUT, `${label}必须是对象`);
545
+ return value;
546
+ }
547
+ function typedRequiredString(value, label) {
548
+ if (typeof value !== 'string')
549
+ throw new DomainError(DomainErrorCodes.INVALID_INPUT, `${label}必须是字符串`);
550
+ const result = value.trim();
551
+ if (!result)
552
+ throw new DomainError(DomainErrorCodes.INVALID_INPUT, `${label}不能为空`);
553
+ return result;
554
+ }
555
+ function nullableString(value, label) {
556
+ if (value == null)
557
+ return null;
558
+ if (typeof value !== 'string')
559
+ throw new DomainError(DomainErrorCodes.INVALID_INPUT, `${label}必须是字符串`);
560
+ return value.trim() || null;
561
+ }
562
+ function nullableDecimalString(value, label) {
563
+ if (value == null)
564
+ return null;
565
+ if (typeof value !== 'string' || !/^(?:0|[1-9]\d{0,15})(?:\.\d{1,4})?$/.test(value.trim()))
566
+ throw new DomainError(DomainErrorCodes.INVALID_INPUT, `${label}必须是最多四位小数的非负数字符串`);
567
+ return value.trim();
568
+ }
212
569
  function stringValue(value) { const result = String(value ?? '').trim(); return result || undefined; }
213
570
  function numberValue(value, fallback) { const result = Number(value); return Number.isFinite(result) ? result : fallback; }
214
571
  function fileNameOf(file, input) { return String(file?.originalname ?? file?.name ?? input.fileName ?? 'uploaded-file'); }
@@ -216,7 +573,26 @@ function bufferOf(file) { const value = file?.buffer; if (Buffer.isBuffer(value)
216
573
  return value; if (value instanceof ArrayBuffer)
217
574
  return Buffer.from(value); if (ArrayBuffer.isView(value))
218
575
  return Buffer.from(value.buffer, value.byteOffset, value.byteLength); return null; }
219
- async function bufferFromPath(input) { const workspace = record(input.workspaceFile); const path = stringValue(workspace.workspacePath ?? workspace.filePath ?? input.workspacePath ?? input.filePath); return path ? readFile(path) : null; }
220
576
  async function readPackageFile(packageName, filePath) { const root = dirname(requireFromHere.resolve(`${packageName}/package.json`)); return readFile(join(root, filePath), 'utf8'); }
221
- function hsConfig() { return { baseUrl: process.env.TRADE_COMPLIANCE_HS_BASE_URL, timeoutMs: Number(process.env.TRADE_COMPLIANCE_HS_TIMEOUT_MS || 15000) }; }
577
+ async function sourcePreview(fileName, buffer) {
578
+ const format = formatForSignature(buffer);
579
+ if (format === 'PDF' || format === 'PNG' || format === 'JPEG' || format === 'WEBP') {
580
+ return { previewKind: 'URL', fileName, mimeType: mimeTypeForFormat(format), base64: buffer.toString('base64') };
581
+ }
582
+ if (format === 'DOC' || format === 'DOCX' || format === 'XLS' || format === 'XLSX') {
583
+ const extracted = await extractDocumentText({ buffer, fileName, mimeType: mimeTypeForFormat(format), maxChars: 100000 });
584
+ const content = (extracted.chunks?.join('\n\n') ?? extracted.text ?? '').trim();
585
+ if (!content)
586
+ return { previewKind: 'UNAVAILABLE', fileName, message: '该 Office 文件未提取到可预览文本' };
587
+ return { previewKind: 'TEXT', fileName, content };
588
+ }
589
+ return { previewKind: 'UNAVAILABLE', fileName, message: '该源文件暂无法在线预览' };
590
+ }
591
+ function mimeTypeForFormat(format) {
592
+ const values = {
593
+ PDF: 'application/pdf', DOC: 'application/msword', DOCX: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', XLS: 'application/vnd.ms-excel', XLSX: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', PNG: 'image/png', JPEG: 'image/jpeg', WEBP: 'image/webp'
594
+ };
595
+ return values[format];
596
+ }
597
+ export function sanitizeIframeCss(css) { return css.replace(/<\/style/gi, (match) => `<\\/${match.slice(2)}`); }
222
598
  //# sourceMappingURL=workbench-view.provider.js.map