openxiangda 1.0.141 → 1.0.142

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 (28) hide show
  1. package/README.md +52 -2
  2. package/lib/cli.js +89 -0
  3. package/lib/design-gates.js +29 -0
  4. package/openxiangda-skills/SKILL.md +3 -1
  5. package/package.json +1 -1
  6. package/packages/sdk/dist/{ProcessPreview-CsOTCTRT.d.mts → ProcessPreview-CUQFUDvw.d.mts} +5 -2
  7. package/packages/sdk/dist/{ProcessPreview-CsOTCTRT.d.ts → ProcessPreview-CUQFUDvw.d.ts} +5 -2
  8. package/packages/sdk/dist/components/index.cjs +59 -19
  9. package/packages/sdk/dist/components/index.cjs.map +1 -1
  10. package/packages/sdk/dist/components/index.d.mts +4 -4
  11. package/packages/sdk/dist/components/index.d.ts +4 -4
  12. package/packages/sdk/dist/components/index.mjs +59 -19
  13. package/packages/sdk/dist/components/index.mjs.map +1 -1
  14. package/packages/sdk/dist/{dataManagementApi-Cd1axl3v.d.ts → dataManagementApi-CwBEmnXg.d.ts} +1 -1
  15. package/packages/sdk/dist/{dataManagementApi-C1OKWpP7.d.mts → dataManagementApi-DQKInwWS.d.mts} +1 -1
  16. package/packages/sdk/dist/runtime/index.cjs +75 -20
  17. package/packages/sdk/dist/runtime/index.cjs.map +1 -1
  18. package/packages/sdk/dist/runtime/index.d.mts +3 -3
  19. package/packages/sdk/dist/runtime/index.d.ts +3 -3
  20. package/packages/sdk/dist/runtime/index.mjs +75 -20
  21. package/packages/sdk/dist/runtime/index.mjs.map +1 -1
  22. package/packages/sdk/dist/runtime/react.cjs +16 -1
  23. package/packages/sdk/dist/runtime/react.cjs.map +1 -1
  24. package/packages/sdk/dist/runtime/react.d.mts +17 -2
  25. package/packages/sdk/dist/runtime/react.d.ts +17 -2
  26. package/packages/sdk/dist/runtime/react.mjs +16 -1
  27. package/packages/sdk/dist/runtime/react.mjs.map +1 -1
  28. package/templates/openxiangda-react-spa/src/pages/defaults/FormRoutePage.tsx +9 -0
package/README.md CHANGED
@@ -123,6 +123,8 @@ openxiangda permission audit --json
123
123
 
124
124
  调用和测试类命令会检查 JSON envelope。HTTP 200 但 `code: "PUBLIC_GRANT_DENIED"`、`success: false` 或其他字符串业务错误码会被当作失败。
125
125
 
126
+ 角色资源里的 `apiPermissionCodes` 依赖平台端已存在的 API 权限点。比如校区管理员需要维护应用角色时,manifest 应声明 `app:role:manage`;如果 `resource publish` 提示缺少这个权限点,说明当前平台租户的 `api_permissions` seed 不完整,通常是平台版本未包含 seed、服务启动 seed 没跑成功,或租户创建在 seed 之后。处理方式是升级/重启平台并补齐权限点 seed,或由平台管理员补入 `app:role:manage`、`app:page-permission-group:manage`、`app:form-permission-group:manage` 等应用级权限点;不要为了通过发布删除 manifest 里的权限声明。
127
+
126
128
  Codex skills are installed separately from login/profile state. Run `openxiangda skill install` after installing or linking the CLI. It installs the `openxiangda` root skill plus the 8 top-level subskills into `${CODEX_HOME:-~/.codex}/skills` by default:
127
129
 
128
130
  - `openxiangda`
@@ -228,10 +230,47 @@ const affiliatedDepartmentExternalId = user?.affiliatedDepartment?.externalId
228
230
 
229
231
  工程化资源放在工作区 `src/resources/` 下,由 `openxiangda resource validate|plan|publish|pull` 管理。`workspace publish` 会先构建并注册 workspace 表单/页面,再执行非破坏性资源 upsert,这样菜单、权限组、流程和表单设置可以解析最新的 profile-local ID。需要删除平台中 manifest 未声明的资源时,显式传 `--prune`。连接器页面运行时通过 `sdk.connector.invoke()` / `sdk.connector.call("connector.api")` 调用平台运行时接口,第三方密钥只保存在后端连接器配置中。
230
232
 
231
- React SPA 新应用的无需登录访问统一使用 `/view/:appType/public/*`。应用在 `src/resources/routes/*.json` 声明公开路由,在 `src/resources/public-access/*.json` 声明公开策略、外部角色和可访问资源 grant,页面用 `PublicAccessGate` 或 `createPublicAccessClient` 创建 scoped public session。React SPA 路由树必须在 `OpenXiangdaProvider` 内再包一层 `OpenXiangdaPageProvider`;页面只要调用 `usePageSdk()`、`usePageContext()`、`useDataSource()` 等 Page SDK hooks,就依赖这层 provider。公开 guest 的 form、dataView、function、connector 默认全部拒绝,只有 policy `grants` 明确列出的资源可访问,并且仍受对应表单权限组、dataView 权限组等后端权限控制。
233
+ React SPA 新应用的无需登录访问统一使用 `/view/:appType/public/*`。应用在 `src/resources/routes/*.json` 声明公开路由,在 `src/resources/public-access/*.json` 声明公开策略、外部角色和可访问资源 grant,页面用 `PublicAccessGate` 或 `createPublicAccessClient` 创建 scoped public session。React SPA 路由树必须在 `OpenXiangdaProvider` 内再包一层 `OpenXiangdaPageProvider`;页面只要调用 `usePageSdk()`、`usePageContext()`、`useDataSource()` 等 Page SDK hooks,就依赖这层 provider。公开 guest 的 form、dataView、function、connector、storage 默认全部拒绝,只有 policy `grants` 明确列出的资源可访问,并且仍受对应表单权限组、dataView 权限组、storage grant 等后端权限控制。
232
234
 
233
235
  `mode: "ticket"` 的公开策略默认按单次 ticket 使用;只有明确配置 `ticketConfig.singleUse: false` 时才允许复用。新 public session 只返回 bearer token,SDK 会把 token 注入后续 runtime/bootstrap、dataView、function、connector 请求,不依赖认证 cookie。
234
236
 
237
+ 公开页上传图片或附件时,不要放开游客全局 `/service/file/upload`。在 public-access policy 的 `grants.storage` 中声明 bucket、动作、MIME、扩展名、大小、visibility 和 `pathPrefix`,`ImageField` / `AttachmentField` 会通过 `OpenXiangdaProvider` 注入的 public token 调用上传接口。服务端会生成 objectName,前端不要传入路径;私有文件预览/下载通过 access ticket,不直接暴露对象地址。安全默认值是:公开报名、公开采集等场景上传到私有 bucket,按公开会话授权短时预览或下载。
238
+
239
+ ```json
240
+ {
241
+ "grants": {
242
+ "storage": [
243
+ {
244
+ "bucketName": "images",
245
+ "actions": ["upload", "preview"],
246
+ "allowedMimeTypes": ["image/jpeg", "image/png", "image/webp"],
247
+ "allowedExtensions": ["jpg", "jpeg", "png", "webp"],
248
+ "maxSizeBytes": 15728640,
249
+ "visibility": "private",
250
+ "pathPrefix": "public/register/images/"
251
+ },
252
+ {
253
+ "bucketName": "attachments",
254
+ "actions": ["upload", "preview", "download"],
255
+ "allowedMimeTypes": [
256
+ "application/pdf",
257
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
258
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
259
+ "application/vnd.openxmlformats-officedocument.presentationml.presentation",
260
+ "text/plain",
261
+ "text/csv",
262
+ "application/json"
263
+ ],
264
+ "allowedExtensions": ["pdf", "docx", "xlsx", "pptx", "txt", "csv", "json"],
265
+ "maxSizeBytes": 31457280,
266
+ "visibility": "private",
267
+ "pathPrefix": "public/register/attachments/"
268
+ }
269
+ ]
270
+ }
271
+ }
272
+ ```
273
+
235
274
  公开访问和测试脚本不能只判断 HTTP 状态。平台部分运行时接口可能返回 HTTP 200,同时在 JSON envelope 中返回业务错误码,例如 `code: "PUBLIC_GRANT_DENIED"`。只有 `code` 为 `200`、`"200"` 或兼容成功码 `0` 时才算成功;字符串错误码、`success: false` 或非 2xx HTTP 都必须当作失败。
236
275
 
237
276
  旧 `?publicAccess=guest` 和表单 `settings/forms/*.json` 里的 `publicAccess` 只用于旧 `sy-lowcode-view` 兼容。新 React SPA 应用不要再设计或生成这种链接。
@@ -280,7 +319,18 @@ import { ImageField } from "openxiangda"
280
319
  "forms": ["registration_form"],
281
320
  "dataViews": ["public_registration_lookup"],
282
321
  "functions": ["submit_public_registration"],
283
- "connectors": ["sms.sendCode"]
322
+ "connectors": ["sms.sendCode"],
323
+ "storage": [
324
+ {
325
+ "bucketName": "images",
326
+ "actions": ["upload", "preview"],
327
+ "allowedMimeTypes": ["image/jpeg", "image/png", "image/webp"],
328
+ "allowedExtensions": ["jpg", "jpeg", "png", "webp"],
329
+ "maxSizeBytes": 15728640,
330
+ "visibility": "private",
331
+ "pathPrefix": "public/register/images/"
332
+ }
333
+ ]
284
334
  }
285
335
  }
286
336
  ```
package/lib/cli.js CHANGED
@@ -5861,17 +5861,64 @@ function buildPublicGrantChecks(bound, grants, flags = {}) {
5861
5861
  for (const code of splitList(flags.connector || flags['connector-code'])) {
5862
5862
  checks.push({ type: 'connector', code, allowed: (grants.connectors || []).includes(code) });
5863
5863
  }
5864
+ const storageGrants = Array.isArray(grants.storage) ? grants.storage : [];
5865
+ for (const bucketName of splitList(flags['storage-bucket'] || flags.bucket)) {
5866
+ const action = String(flags['storage-action'] || flags.action || 'upload').trim();
5867
+ const extension = String(flags.extension || flags.ext || '').replace(/^\./, '').toLowerCase();
5868
+ const mimeType = String(flags.mime || flags['mime-type'] || '').trim().toLowerCase();
5869
+ const visibility = String(flags.visibility || '').trim();
5870
+ const size = Number(flags.size || flags['size-bytes'] || 0);
5871
+ checks.push({
5872
+ type: 'storage',
5873
+ bucketName,
5874
+ action,
5875
+ allowed: storageGrants.some(grant =>
5876
+ publicStorageGrantAllows(grant, {
5877
+ bucketName,
5878
+ action,
5879
+ extension,
5880
+ mimeType,
5881
+ visibility,
5882
+ size,
5883
+ })
5884
+ ),
5885
+ });
5886
+ }
5864
5887
  if (checks.length === 0) {
5865
5888
  return [
5866
5889
  { type: 'forms', granted: grants.forms || [], allowed: true },
5867
5890
  { type: 'dataViews', granted: grants.dataViews || [], allowed: true },
5868
5891
  { type: 'functions', granted: grants.functions || [], allowed: true },
5869
5892
  { type: 'connectors', granted: grants.connectors || [], allowed: true },
5893
+ { type: 'storage', granted: storageGrants, allowed: true },
5870
5894
  ];
5871
5895
  }
5872
5896
  return checks;
5873
5897
  }
5874
5898
 
5899
+ function publicStorageGrantAllows(grant, input) {
5900
+ if (!grant || grant.bucketName !== input.bucketName) return false;
5901
+ const actions = Array.isArray(grant.actions) ? grant.actions : [];
5902
+ if (!actions.includes(input.action)) return false;
5903
+ if (input.visibility && String(grant.visibility || 'private') !== input.visibility) return false;
5904
+ if (input.size > 0 && Number(grant.maxSizeBytes || 0) > 0 && input.size > Number(grant.maxSizeBytes)) {
5905
+ return false;
5906
+ }
5907
+ if (input.extension) {
5908
+ const extensions = (grant.allowedExtensions || []).map(value =>
5909
+ String(value || '').replace(/^\./, '').toLowerCase()
5910
+ );
5911
+ if (!extensions.includes(input.extension)) return false;
5912
+ }
5913
+ if (input.mimeType) {
5914
+ const mimeTypes = (grant.allowedMimeTypes || []).map(value =>
5915
+ String(value || '').trim().toLowerCase()
5916
+ );
5917
+ if (!mimeTypes.includes(input.mimeType)) return false;
5918
+ }
5919
+ return true;
5920
+ }
5921
+
5875
5922
  async function buildPermissionAudit(config, target, flags = {}) {
5876
5923
  const manifest = loadWorkspaceResources();
5877
5924
  const validation = validateWorkspaceResources(manifest);
@@ -10682,9 +10729,51 @@ function normalizePublicAccessGrants(bound, grants = {}) {
10682
10729
  dataViews: normalizePermissionCodeArray(grants.dataViews),
10683
10730
  functions: normalizePermissionCodeArray(grants.functions),
10684
10731
  connectors: normalizePermissionCodeArray(grants.connectors),
10732
+ storage: normalizePublicStorageGrants(grants.storage),
10685
10733
  });
10686
10734
  }
10687
10735
 
10736
+ function normalizePublicStorageGrants(storage = []) {
10737
+ if (!Array.isArray(storage)) return [];
10738
+ return storage
10739
+ .map(item => {
10740
+ const bucketName = String(item?.bucketName || '').trim();
10741
+ if (!bucketName) return null;
10742
+ return stripUndefinedValues({
10743
+ bucketName,
10744
+ actions: normalizePublicStorageActions(item.actions),
10745
+ allowedMimeTypes: normalizePermissionCodeArray(item.allowedMimeTypes).map(value =>
10746
+ value.toLowerCase()
10747
+ ),
10748
+ allowedExtensions: normalizePermissionCodeArray(item.allowedExtensions).map(value =>
10749
+ value.replace(/^\./, '').toLowerCase()
10750
+ ),
10751
+ maxSizeBytes: normalizePublicStoragePositiveInteger(item.maxSizeBytes),
10752
+ visibility: String(item.visibility || '').trim() === 'public' ? 'public' : 'private',
10753
+ pathPrefix: normalizePublicStoragePathPrefix(item.pathPrefix),
10754
+ });
10755
+ })
10756
+ .filter(Boolean);
10757
+ }
10758
+
10759
+ function normalizePublicStorageActions(actions) {
10760
+ const allowed = new Set(['upload', 'preview', 'download']);
10761
+ const normalized = normalizePermissionCodeArray(actions).filter(action => allowed.has(action));
10762
+ return normalized.length > 0 ? unique(normalized) : ['upload'];
10763
+ }
10764
+
10765
+ function normalizePublicStoragePositiveInteger(value) {
10766
+ const numeric = Number(value);
10767
+ if (!Number.isFinite(numeric) || numeric <= 0) return undefined;
10768
+ return Math.floor(numeric);
10769
+ }
10770
+
10771
+ function normalizePublicStoragePathPrefix(value) {
10772
+ const normalized = String(value || '').trim().replace(/^\/+/, '');
10773
+ if (!normalized) return undefined;
10774
+ return normalized.endsWith('/') ? normalized : `${normalized}/`;
10775
+ }
10776
+
10688
10777
  function normalizeAuthConfigManifest(authConfig) {
10689
10778
  const code = authConfig.code || authConfig.resourceCode || 'default';
10690
10779
  const configJson = clonePlainJson(
@@ -459,12 +459,41 @@ const RESOURCE_EXPLAINS = {
459
459
  dataViews: ['public_lookup'],
460
460
  functions: [],
461
461
  connectors: [],
462
+ storage: [
463
+ {
464
+ bucketName: 'images',
465
+ actions: ['upload', 'preview'],
466
+ allowedMimeTypes: ['image/jpeg', 'image/png', 'image/webp'],
467
+ allowedExtensions: ['jpg', 'jpeg', 'png', 'webp'],
468
+ maxSizeBytes: 15728640,
469
+ visibility: 'private',
470
+ pathPrefix: 'public/register/images/',
471
+ },
472
+ {
473
+ bucketName: 'attachments',
474
+ actions: ['upload', 'preview', 'download'],
475
+ allowedMimeTypes: [
476
+ 'application/pdf',
477
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
478
+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
479
+ 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
480
+ 'text/plain',
481
+ 'text/csv',
482
+ 'application/json',
483
+ ],
484
+ allowedExtensions: ['pdf', 'docx', 'xlsx', 'pptx', 'txt', 'csv', 'json'],
485
+ maxSizeBytes: 31457280,
486
+ visibility: 'private',
487
+ pathPrefix: 'public/register/attachments/',
488
+ },
489
+ ],
462
490
  },
463
491
  },
464
492
  commands: [
465
493
  'openxiangda public-access upsert --json-file src/resources/public-access/public_register.json',
466
494
  'openxiangda public-access session-test public_register --path /view/APP_XXX/public/register',
467
495
  'openxiangda public-access grant-check public_register --form-code customer',
496
+ '公开页 ImageField/AttachmentField 上传走 public session bearer token;storage grant 按 bucket/action/MIME/ext/size/visibility/pathPrefix 限制。',
468
497
  'React 页面侧使用 PublicAccessGate;如果使用 Page SDK hooks,放在 OpenXiangdaProvider + OpenXiangdaPageProvider 内',
469
498
  ],
470
499
  },
@@ -50,7 +50,7 @@ OpenXiangda supports two workspace modes. Classic `sy-lowcode-app-workspace` pub
50
50
  - ✅ Run `openxiangda update check --json` at the start of substantial work; if `updateAvailable`, run `openxiangda update install` and `openxiangda skill install --force`.
51
51
  - ✅ For non-trivial app requirements, run the architecture design gate first: forms, fields, pages, permissions, data views, status/workflow, automation, notifications, and development tasks must be decided before coding.
52
52
  - ✅ For app login/auth requirements, run the auth security gate before coding: enabled methods, registration policy, identity matching keys, provider boundary, default roles, rate limits, audit fields, and third-party ownership must be confirmed.
53
- - ✅ For public/no-login access requirements, run the public access design gate before coding: public routes, external role codes, guest vs ticket, form/dataView/function/connector grants, backend permission groups, and visible loading/error fallback states must be confirmed. New React SPA apps use `/view/:appType/public/*`, `src/resources/routes/`, `src/resources/public-access/`, and `PublicAccessGate`; route children that use Page SDK hooks must be inside `OpenXiangdaProvider` + `OpenXiangdaPageProvider`.
53
+ - ✅ For public/no-login access requirements, run the public access design gate before coding: public routes, external role codes, guest vs ticket, form/dataView/function/connector/storage grants, backend permission groups, and visible loading/error fallback states must be confirmed. New React SPA apps use `/view/:appType/public/*`, `src/resources/routes/`, `src/resources/public-access/`, and `PublicAccessGate`; route children that use Page SDK hooks must be inside `OpenXiangdaProvider` + `OpenXiangdaPageProvider`.
54
54
  - ✅ For account/role/permission/data-scope/query-param authorization requirements, run `openxiangda design gates --topic permissions --json` before coding. Pick one mode from `managed-platform-account`, `existing-platform-user-assignment`, `static-role-permission`, or `query-param-context`, write the permission matrix, and use roles/page-groups/form-groups/App Functions for real enforcement.
55
55
  - ✅ Public/no-login validation must inspect the JSON envelope, not only HTTP status. HTTP 200 with `code: "PUBLIC_GRANT_DENIED"` is a denied request; success requires `code` `200`, `"200"`, or compatible `0`, and no `success: false`.
56
56
  - ✅ Use first-class resource CLI commands for discovery, diagnosis, and small live fixes: `route`, `public-access`, `auth-config`, `function`, `connector`, `notification`, `organization`, `data-view`, `menu`, and `permission`. For multi-resource formal development, still prefer `src/resources/**` + `openxiangda resource validate|plan|publish`. If a direct CLI write is intentionally used, add `--write-manifest` when the repository should stay the source of truth.
@@ -151,6 +151,7 @@ When the user provides a root domain such as `https://yida.wisejob.cn/`, use it
151
151
  - Never store token data in the project directory. User tokens live in `~/.openxiangda/profiles.json`; project state lives in `.openxiangda/state.json` and stores only IDs and mappings.
152
152
  - Shared workspace env values such as `APP_OSS_*` should live in `~/.openxiangda/.env` by default. Project `.env` files are only per-workspace overrides.
153
153
  - For custom OSS image or attachment upload, declare `src/resources/storage/<code>.json`, then set `uploadProvider: "oss"` and `storageCode` on `AttachmentField` or `ImageField`. If `imageCompression` outputs `webp` or another explicit format, include that extension in storage `allowedExtensions`.
154
+ - For public/no-login image or attachment uploads, declare `grants.storage` in `src/resources/public-access/<code>.json`; do not open `/service/file/upload` globally. Use conservative private bucket grants by default, with explicit bucket, actions, MIME types, extensions, max size, visibility, and pathPrefix. `ImageField` / `AttachmentField` uploads inside `PublicAccessGate` use the public session bearer token injected by `OpenXiangdaProvider`.
154
155
  - For suspected platform defects, bugs, missing capabilities, unclear design rules, repeated workarounds, AI uncertainty, or product optimization requests, proactively run `openxiangda feedback submit --yes` with a concise summary and detailed description. Include the command, error, relevant files, and logs/context files when available. If robot env is missing or submission fails, run/record `openxiangda feedback preview` and tell the user what could not be sent. After successful submission, tell the user what was reported and include the feedback fingerprint. The CLI redacts tokens, cookies, secrets, phone numbers, and emails before sending.
155
156
  - For form pages, every visible field should have a concise user-facing `placeholder`. Use `tips` only for special constraints or non-obvious business rules; do not add tips to every field.
156
157
  - Form pages should display only fields the user needs to see. Use `SelectField` / `RadioField` for enums. For data maintained by another form, such as class, college, customer, or project, use `SelectField` with `optionSource.type: "linkedForm"` so the runtime queries form data through the SDK and builds dropdown options; set `remoteSearch: true` and an explicit `searchFieldId` when the source data can be large. Do not make users maintain raw ID text fields, and do not use `AssociationFormField` for new form work.
@@ -165,6 +166,7 @@ When the user provides a root domain such as `https://yida.wisejob.cn/`, use it
165
166
  - For app-managed platform departments/accounts, use `sdk.organization.departments.*`, `sdk.organization.accounts.*`, `ctx.organization.departments.*`, or `ctx.organization.accounts.*`. The real current user/audit actor must hold `app:organization:manage` on the app; runtime service principals do not bypass this permission. Do not call legacy `/user` or `/department` write endpoints from apps. Do not include `password` in account updates; reset target accounts with `resetPassword`, and change the current user's password with `changeMyPassword({ oldPassword, newPassword })`.
166
167
  - For account, role, data-scope, organization-account, RBAC, or query-param authorization designs, read `references/permission-design-patterns.md` and choose a permission mode before writing forms/pages/resources. Formal backends should use platform roles, page permission groups, form permission groups, public-access grants, and App Function role/scope checks; frontend hiding is display only.
167
168
  - When a role such as 校区管理员 will create app roles, assign role members, grant role permissions, maintain permission groups, or manage organization accounts, its role manifest must declare the needed `apiPermissionCodes` such as `app:role:manage`, `app:page-permission-group:manage`, `app:form-permission-group:manage`, and `app:organization:manage`. If that administrator creates another administrator role, the new role must also declare its own `apiPermissionCodes`; permissions are not inherited from the creator.
169
+ - If `resource publish` reports a missing API permission point such as `app:role:manage`, treat it as a platform seed/data problem. The platform tenant must have the permission code in `api_permissions`; do not remove the role manifest permission just to make publish pass.
168
170
  - For application login, declare auth resources in `src/resources/auth/<code>.json` and publish them with `openxiangda resource publish`. App Function auth providers may validate external credentials and return only an identity assertion; they must never issue tokens, set cookies, or write platform user/binding tables directly. The platform auth service decides create/bind/reject and issues tokens.
169
171
  - For external/no-login pages in React SPA apps, declare `src/resources/routes/<code>.json` and `src/resources/public-access/<code>.json`, put the page under `/view/:appType/public/*`, and use `PublicAccessGate` or `createPublicAccessClient`. The route tree must also include `OpenXiangdaPageProvider` inside `OpenXiangdaProvider` before any `usePageSdk()` / `usePageContext()` / `useDataSource()` call; otherwise pages throw `usePageSdkStore 必须在 PageProvider 内使用`. Always provide `fallback` and `errorFallback` for public routes so slow networks, missing tickets, or expired tickets do not render blank pages. Public guest access to forms, dataViews, functions, and connectors is denied unless policy `grants` explicitly names the resource; backend permission groups still apply.
170
172
  - When verifying public access, parse the response body and reject string business errors such as `PUBLIC_GRANT_DENIED` even if the HTTP status is 200. Do not use `response.ok` alone as the success condition.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openxiangda",
3
- "version": "1.0.141",
3
+ "version": "1.0.142",
4
4
  "description": "OpenXiangda CLI, workspace build tools, runtime SDK, and form components.",
5
5
  "private": false,
6
6
  "bin": {
@@ -248,9 +248,10 @@ interface RuntimeRequestConfig {
248
248
  method?: string;
249
249
  params?: Record<string, any>;
250
250
  data?: any;
251
- headers?: Record<string, string>;
251
+ headers?: HeadersInit;
252
252
  responseType?: 'json' | 'blob';
253
253
  }
254
+ type RuntimeAuthHeadersProvider = () => HeadersInit | undefined;
254
255
  type RuntimeUploadProvider = 'platform' | 'oss' | 'builtin-oss';
255
256
  interface RuntimeUploadOptions {
256
257
  uploadProvider?: RuntimeUploadProvider;
@@ -293,6 +294,8 @@ interface FormRuntimeApi {
293
294
  }
294
295
  type FormRuntimeApiConfig = Partial<FormRuntimeApi> & {
295
296
  baseUrl?: string;
297
+ fetchImpl?: typeof fetch;
298
+ getAuthHeaders?: RuntimeAuthHeadersProvider;
296
299
  };
297
300
  /** 表单引擎配置 */
298
301
  interface FormEngineConfig {
@@ -1171,4 +1174,4 @@ interface ProcessPreviewProps {
1171
1174
  }
1172
1175
  declare const ProcessPreview: React__default.FC<ProcessPreviewProps>;
1173
1176
 
1174
- export { type DepartmentSelectFieldProps as $, type ApprovalPermission as A, type ReturnParams as B, type ChangeRecordQueryParams as C, type SaveTaskParams as D, type TransferParams as E, type FieldDefinition as F, type TextFieldProps as G, type TextAreaFieldProps as H, type InitiatorSelectCandidate as I, type SelectFieldProps as J, type RadioFieldProps as K, type CheckboxFieldProps as L, type MultiSelectFieldProps as M, type NumberFieldProps as N, type OptionItem as O, type ProcessStatus as P, type DateFieldProps as Q, type RuntimeResponse as R, type StatusMeta as S, type TaskStatus as T, type CascadeDateFieldProps as U, type ValidationPreset as V, type WithdrawParams as W, type AttachmentFieldProps as X, type ImageFieldProps as Y, type SubFormFieldProps as Z, type UserSelectFieldProps as _, type FormRuntimeApi as a, ProcessPreview as a$, type CascadeSelectFieldProps as a0, type AddressFieldProps as a1, type AssociationFormFieldProps as a2, type EditorFieldProps as a3, type EditorChoiceOption as a4, type SerialNumberFieldProps as a5, type LocationFieldProps as a6, type DigitalSignatureFieldProps as a7, type JSONFieldProps as a8, type ProcessAction as a9, type FieldLayoutNode as aA, type FieldValueSyncConfig as aB, type FormEffectAction as aC, type FormEffectCondition as aD, type FormEffectConditionOperator as aE, type FormEngineMode as aF, type FormLayoutNode as aG, FormSection as aH, type FormSectionProps as aI, type FormSubmitBehavior as aJ, type FormTemplateConfig as aK, type GridLayoutCell as aL, type GridLayoutNode as aM, type ImageCompressionConfig as aN, type ImageCompressionVariantConfig as aO, type ImageVariant as aP, type InitiatorSelectScope as aQ, type LayoutVisibleWhen as aR, type LinkedFormOptionConfig as aS, type LocationValue as aT, type LowcodePageMeta as aU, type LowcodePageNode as aV, type LowcodePageNodeType as aW, type OptionSourceType as aX, type PeopleShortcutConfig as aY, type PeopleShortcutType as aZ, type ProcessNodeType as a_, type InitiatorSelectedApprovers as aa, type ReturnPolicy as ab, type ChangeRecord as ac, type StandardFormPageMode as ad, type LowcodePageSchema as ae, type AddressValue as af, type ApprovalActionType as ag, ApprovalTimeline as ah, type ApprovalTimelineProps as ai, type AssociationFormConfig as aj, type AssociationValue as ak, type AttachmentImageVariants as al, type AttachmentItem as am, type BaseFieldProps as an, type BaseLayoutNode as ao, type DataFilter as ap, type DataLinkageCondition as aq, type DataLinkageConfig as ar, type DateRangeRestriction as as, type DateRestrictionConfig as at, type DateShortcutConfig as au, type DateShortcutType as av, type DefaultValueLinkageConfig as aw, type DepartmentTreeNode as ax, type DigitalSignatureValue as ay, type EditorToolbarAction as az, type FormSchema as b, type ProcessPreviewProps as b0, type RuntimeDataQueryParams as b1, type RuntimeDataQueryResult as b2, type RuntimeRequestConfig as b3, type RuntimeUploadOptions as b4, type RuntimeUploadProvider as b5, type SectionLayoutNode as b6, type SignaturePoint as b7, type StepLayoutItem as b8, type StepsLayoutNode as b9, type SubFormColumn as ba, type TabLayoutItem as bb, type TabsLayoutNode as bc, type TextShortcutConfig as bd, type TextShortcutType as be, type UserDisplayFormat as bf, type UserItem as bg, type FormRuntimeApiConfig as c, type FormEngineConfig as d, type FieldBehavior as e, type FormRuntimeConfig as f, type FormAppearanceConfig as g, type ValidationRule as h, type FormEffect as i, type OptionSourceConfig as j, type FormDataDeleteParams as k, type ChangeRecordListResponse as l, type FormDataQueryParams as m, type FormInstanceData as n, type InitiatorSelectRequirement as o, type ProcessBasicInfo as p, type ProcessDefinition as q, type ProcessTask as r, type ReturnableNodeResult as s, type ReturnableNode as t, type ViewPermissionQueryParams as u, type ViewPermissionSummary as v, type ApproveParams as w, type PreviewParams as x, type ProcessRoute as y, type ResubmitParams as z };
1177
+ export { type DepartmentSelectFieldProps as $, type ApprovalPermission as A, type ReturnParams as B, type ChangeRecordQueryParams as C, type SaveTaskParams as D, type TransferParams as E, type FieldDefinition as F, type TextFieldProps as G, type TextAreaFieldProps as H, type InitiatorSelectCandidate as I, type SelectFieldProps as J, type RadioFieldProps as K, type CheckboxFieldProps as L, type MultiSelectFieldProps as M, type NumberFieldProps as N, type OptionItem as O, type ProcessStatus as P, type DateFieldProps as Q, type RuntimeResponse as R, type StatusMeta as S, type TaskStatus as T, type CascadeDateFieldProps as U, type ValidationPreset as V, type WithdrawParams as W, type AttachmentFieldProps as X, type ImageFieldProps as Y, type SubFormFieldProps as Z, type UserSelectFieldProps as _, type FormRuntimeApi as a, ProcessPreview as a$, type CascadeSelectFieldProps as a0, type AddressFieldProps as a1, type AssociationFormFieldProps as a2, type EditorFieldProps as a3, type EditorChoiceOption as a4, type SerialNumberFieldProps as a5, type LocationFieldProps as a6, type DigitalSignatureFieldProps as a7, type JSONFieldProps as a8, type ProcessAction as a9, type FieldLayoutNode as aA, type FieldValueSyncConfig as aB, type FormEffectAction as aC, type FormEffectCondition as aD, type FormEffectConditionOperator as aE, type FormEngineMode as aF, type FormLayoutNode as aG, FormSection as aH, type FormSectionProps as aI, type FormSubmitBehavior as aJ, type FormTemplateConfig as aK, type GridLayoutCell as aL, type GridLayoutNode as aM, type ImageCompressionConfig as aN, type ImageCompressionVariantConfig as aO, type ImageVariant as aP, type InitiatorSelectScope as aQ, type LayoutVisibleWhen as aR, type LinkedFormOptionConfig as aS, type LocationValue as aT, type LowcodePageMeta as aU, type LowcodePageNode as aV, type LowcodePageNodeType as aW, type OptionSourceType as aX, type PeopleShortcutConfig as aY, type PeopleShortcutType as aZ, type ProcessNodeType as a_, type InitiatorSelectedApprovers as aa, type ReturnPolicy as ab, type ChangeRecord as ac, type StandardFormPageMode as ad, type LowcodePageSchema as ae, type AddressValue as af, type ApprovalActionType as ag, ApprovalTimeline as ah, type ApprovalTimelineProps as ai, type AssociationFormConfig as aj, type AssociationValue as ak, type AttachmentImageVariants as al, type AttachmentItem as am, type BaseFieldProps as an, type BaseLayoutNode as ao, type DataFilter as ap, type DataLinkageCondition as aq, type DataLinkageConfig as ar, type DateRangeRestriction as as, type DateRestrictionConfig as at, type DateShortcutConfig as au, type DateShortcutType as av, type DefaultValueLinkageConfig as aw, type DepartmentTreeNode as ax, type DigitalSignatureValue as ay, type EditorToolbarAction as az, type FormSchema as b, type ProcessPreviewProps as b0, type RuntimeAuthHeadersProvider as b1, type RuntimeDataQueryParams as b2, type RuntimeDataQueryResult as b3, type RuntimeRequestConfig as b4, type RuntimeUploadOptions as b5, type RuntimeUploadProvider as b6, type SectionLayoutNode as b7, type SignaturePoint as b8, type StepLayoutItem as b9, type StepsLayoutNode as ba, type SubFormColumn as bb, type TabLayoutItem as bc, type TabsLayoutNode as bd, type TextShortcutConfig as be, type TextShortcutType as bf, type UserDisplayFormat as bg, type UserItem as bh, type FormRuntimeApiConfig as c, type FormEngineConfig as d, type FieldBehavior as e, type FormRuntimeConfig as f, type FormAppearanceConfig as g, type ValidationRule as h, type FormEffect as i, type OptionSourceConfig as j, type FormDataDeleteParams as k, type ChangeRecordListResponse as l, type FormDataQueryParams as m, type FormInstanceData as n, type InitiatorSelectRequirement as o, type ProcessBasicInfo as p, type ProcessDefinition as q, type ProcessTask as r, type ReturnableNodeResult as s, type ReturnableNode as t, type ViewPermissionQueryParams as u, type ViewPermissionSummary as v, type ApproveParams as w, type PreviewParams as x, type ProcessRoute as y, type ResubmitParams as z };
@@ -248,9 +248,10 @@ interface RuntimeRequestConfig {
248
248
  method?: string;
249
249
  params?: Record<string, any>;
250
250
  data?: any;
251
- headers?: Record<string, string>;
251
+ headers?: HeadersInit;
252
252
  responseType?: 'json' | 'blob';
253
253
  }
254
+ type RuntimeAuthHeadersProvider = () => HeadersInit | undefined;
254
255
  type RuntimeUploadProvider = 'platform' | 'oss' | 'builtin-oss';
255
256
  interface RuntimeUploadOptions {
256
257
  uploadProvider?: RuntimeUploadProvider;
@@ -293,6 +294,8 @@ interface FormRuntimeApi {
293
294
  }
294
295
  type FormRuntimeApiConfig = Partial<FormRuntimeApi> & {
295
296
  baseUrl?: string;
297
+ fetchImpl?: typeof fetch;
298
+ getAuthHeaders?: RuntimeAuthHeadersProvider;
296
299
  };
297
300
  /** 表单引擎配置 */
298
301
  interface FormEngineConfig {
@@ -1171,4 +1174,4 @@ interface ProcessPreviewProps {
1171
1174
  }
1172
1175
  declare const ProcessPreview: React__default.FC<ProcessPreviewProps>;
1173
1176
 
1174
- export { type DepartmentSelectFieldProps as $, type ApprovalPermission as A, type ReturnParams as B, type ChangeRecordQueryParams as C, type SaveTaskParams as D, type TransferParams as E, type FieldDefinition as F, type TextFieldProps as G, type TextAreaFieldProps as H, type InitiatorSelectCandidate as I, type SelectFieldProps as J, type RadioFieldProps as K, type CheckboxFieldProps as L, type MultiSelectFieldProps as M, type NumberFieldProps as N, type OptionItem as O, type ProcessStatus as P, type DateFieldProps as Q, type RuntimeResponse as R, type StatusMeta as S, type TaskStatus as T, type CascadeDateFieldProps as U, type ValidationPreset as V, type WithdrawParams as W, type AttachmentFieldProps as X, type ImageFieldProps as Y, type SubFormFieldProps as Z, type UserSelectFieldProps as _, type FormRuntimeApi as a, ProcessPreview as a$, type CascadeSelectFieldProps as a0, type AddressFieldProps as a1, type AssociationFormFieldProps as a2, type EditorFieldProps as a3, type EditorChoiceOption as a4, type SerialNumberFieldProps as a5, type LocationFieldProps as a6, type DigitalSignatureFieldProps as a7, type JSONFieldProps as a8, type ProcessAction as a9, type FieldLayoutNode as aA, type FieldValueSyncConfig as aB, type FormEffectAction as aC, type FormEffectCondition as aD, type FormEffectConditionOperator as aE, type FormEngineMode as aF, type FormLayoutNode as aG, FormSection as aH, type FormSectionProps as aI, type FormSubmitBehavior as aJ, type FormTemplateConfig as aK, type GridLayoutCell as aL, type GridLayoutNode as aM, type ImageCompressionConfig as aN, type ImageCompressionVariantConfig as aO, type ImageVariant as aP, type InitiatorSelectScope as aQ, type LayoutVisibleWhen as aR, type LinkedFormOptionConfig as aS, type LocationValue as aT, type LowcodePageMeta as aU, type LowcodePageNode as aV, type LowcodePageNodeType as aW, type OptionSourceType as aX, type PeopleShortcutConfig as aY, type PeopleShortcutType as aZ, type ProcessNodeType as a_, type InitiatorSelectedApprovers as aa, type ReturnPolicy as ab, type ChangeRecord as ac, type StandardFormPageMode as ad, type LowcodePageSchema as ae, type AddressValue as af, type ApprovalActionType as ag, ApprovalTimeline as ah, type ApprovalTimelineProps as ai, type AssociationFormConfig as aj, type AssociationValue as ak, type AttachmentImageVariants as al, type AttachmentItem as am, type BaseFieldProps as an, type BaseLayoutNode as ao, type DataFilter as ap, type DataLinkageCondition as aq, type DataLinkageConfig as ar, type DateRangeRestriction as as, type DateRestrictionConfig as at, type DateShortcutConfig as au, type DateShortcutType as av, type DefaultValueLinkageConfig as aw, type DepartmentTreeNode as ax, type DigitalSignatureValue as ay, type EditorToolbarAction as az, type FormSchema as b, type ProcessPreviewProps as b0, type RuntimeDataQueryParams as b1, type RuntimeDataQueryResult as b2, type RuntimeRequestConfig as b3, type RuntimeUploadOptions as b4, type RuntimeUploadProvider as b5, type SectionLayoutNode as b6, type SignaturePoint as b7, type StepLayoutItem as b8, type StepsLayoutNode as b9, type SubFormColumn as ba, type TabLayoutItem as bb, type TabsLayoutNode as bc, type TextShortcutConfig as bd, type TextShortcutType as be, type UserDisplayFormat as bf, type UserItem as bg, type FormRuntimeApiConfig as c, type FormEngineConfig as d, type FieldBehavior as e, type FormRuntimeConfig as f, type FormAppearanceConfig as g, type ValidationRule as h, type FormEffect as i, type OptionSourceConfig as j, type FormDataDeleteParams as k, type ChangeRecordListResponse as l, type FormDataQueryParams as m, type FormInstanceData as n, type InitiatorSelectRequirement as o, type ProcessBasicInfo as p, type ProcessDefinition as q, type ProcessTask as r, type ReturnableNodeResult as s, type ReturnableNode as t, type ViewPermissionQueryParams as u, type ViewPermissionSummary as v, type ApproveParams as w, type PreviewParams as x, type ProcessRoute as y, type ResubmitParams as z };
1177
+ export { type DepartmentSelectFieldProps as $, type ApprovalPermission as A, type ReturnParams as B, type ChangeRecordQueryParams as C, type SaveTaskParams as D, type TransferParams as E, type FieldDefinition as F, type TextFieldProps as G, type TextAreaFieldProps as H, type InitiatorSelectCandidate as I, type SelectFieldProps as J, type RadioFieldProps as K, type CheckboxFieldProps as L, type MultiSelectFieldProps as M, type NumberFieldProps as N, type OptionItem as O, type ProcessStatus as P, type DateFieldProps as Q, type RuntimeResponse as R, type StatusMeta as S, type TaskStatus as T, type CascadeDateFieldProps as U, type ValidationPreset as V, type WithdrawParams as W, type AttachmentFieldProps as X, type ImageFieldProps as Y, type SubFormFieldProps as Z, type UserSelectFieldProps as _, type FormRuntimeApi as a, ProcessPreview as a$, type CascadeSelectFieldProps as a0, type AddressFieldProps as a1, type AssociationFormFieldProps as a2, type EditorFieldProps as a3, type EditorChoiceOption as a4, type SerialNumberFieldProps as a5, type LocationFieldProps as a6, type DigitalSignatureFieldProps as a7, type JSONFieldProps as a8, type ProcessAction as a9, type FieldLayoutNode as aA, type FieldValueSyncConfig as aB, type FormEffectAction as aC, type FormEffectCondition as aD, type FormEffectConditionOperator as aE, type FormEngineMode as aF, type FormLayoutNode as aG, FormSection as aH, type FormSectionProps as aI, type FormSubmitBehavior as aJ, type FormTemplateConfig as aK, type GridLayoutCell as aL, type GridLayoutNode as aM, type ImageCompressionConfig as aN, type ImageCompressionVariantConfig as aO, type ImageVariant as aP, type InitiatorSelectScope as aQ, type LayoutVisibleWhen as aR, type LinkedFormOptionConfig as aS, type LocationValue as aT, type LowcodePageMeta as aU, type LowcodePageNode as aV, type LowcodePageNodeType as aW, type OptionSourceType as aX, type PeopleShortcutConfig as aY, type PeopleShortcutType as aZ, type ProcessNodeType as a_, type InitiatorSelectedApprovers as aa, type ReturnPolicy as ab, type ChangeRecord as ac, type StandardFormPageMode as ad, type LowcodePageSchema as ae, type AddressValue as af, type ApprovalActionType as ag, ApprovalTimeline as ah, type ApprovalTimelineProps as ai, type AssociationFormConfig as aj, type AssociationValue as ak, type AttachmentImageVariants as al, type AttachmentItem as am, type BaseFieldProps as an, type BaseLayoutNode as ao, type DataFilter as ap, type DataLinkageCondition as aq, type DataLinkageConfig as ar, type DateRangeRestriction as as, type DateRestrictionConfig as at, type DateShortcutConfig as au, type DateShortcutType as av, type DefaultValueLinkageConfig as aw, type DepartmentTreeNode as ax, type DigitalSignatureValue as ay, type EditorToolbarAction as az, type FormSchema as b, type ProcessPreviewProps as b0, type RuntimeAuthHeadersProvider as b1, type RuntimeDataQueryParams as b2, type RuntimeDataQueryResult as b3, type RuntimeRequestConfig as b4, type RuntimeUploadOptions as b5, type RuntimeUploadProvider as b6, type SectionLayoutNode as b7, type SignaturePoint as b8, type StepLayoutItem as b9, type StepsLayoutNode as ba, type SubFormColumn as bb, type TabLayoutItem as bc, type TabsLayoutNode as bd, type TextShortcutConfig as be, type TextShortcutType as bf, type UserDisplayFormat as bg, type UserItem as bh, type FormRuntimeApiConfig as c, type FormEngineConfig as d, type FieldBehavior as e, type FormRuntimeConfig as f, type FormAppearanceConfig as g, type ValidationRule as h, type FormEffect as i, type OptionSourceConfig as j, type FormDataDeleteParams as k, type ChangeRecordListResponse as l, type FormDataQueryParams as m, type FormInstanceData as n, type InitiatorSelectRequirement as o, type ProcessBasicInfo as p, type ProcessDefinition as q, type ProcessTask as r, type ReturnableNodeResult as s, type ReturnableNode as t, type ViewPermissionQueryParams as u, type ViewPermissionSummary as v, type ApproveParams as w, type PreviewParams as x, type ProcessRoute as y, type ResubmitParams as z };
@@ -10116,7 +10116,31 @@ var parseResponse = async (response) => {
10116
10116
  message: response.statusText
10117
10117
  };
10118
10118
  };
10119
- var createDefaultRequest = (baseUrl) => async (config) => {
10119
+ var applyAuthHeaders = (headers, getAuthHeaders) => {
10120
+ const authHeaders = getAuthHeaders?.();
10121
+ if (authHeaders) {
10122
+ new Headers(authHeaders).forEach((value, key) => {
10123
+ if (!headers.has(key)) headers.set(key, value);
10124
+ });
10125
+ }
10126
+ const token = typeof window !== "undefined" ? window.localStorage?.getItem("token") : void 0;
10127
+ if (token && !headers.has("authorization")) {
10128
+ headers.set("authorization", token);
10129
+ }
10130
+ };
10131
+ var applyXhrAuthHeaders = (xhr, getAuthHeaders) => {
10132
+ let hasAuthorization = false;
10133
+ const authHeaders = getAuthHeaders?.();
10134
+ if (authHeaders) {
10135
+ new Headers(authHeaders).forEach((value, key) => {
10136
+ if (key.toLowerCase() === "authorization") hasAuthorization = true;
10137
+ xhr.setRequestHeader(key, value);
10138
+ });
10139
+ }
10140
+ const token = typeof window !== "undefined" ? window.localStorage?.getItem("token") : void 0;
10141
+ if (token && !hasAuthorization) xhr.setRequestHeader("authorization", token);
10142
+ };
10143
+ var createDefaultRequest = (baseUrl, fetchImpl = fetch, getAuthHeaders) => async (config) => {
10120
10144
  const method = config.method ?? "get";
10121
10145
  const url = appendQuery(joinUrl(baseUrl, config.url), config.params);
10122
10146
  const headers = new Headers(config.headers);
@@ -10129,11 +10153,8 @@ var createDefaultRequest = (baseUrl) => async (config) => {
10129
10153
  body = JSON.stringify(config.data);
10130
10154
  }
10131
10155
  }
10132
- const token = typeof window !== "undefined" ? window.localStorage?.getItem("token") : void 0;
10133
- if (token && !headers.has("authorization")) {
10134
- headers.set("authorization", token);
10135
- }
10136
- const response = await fetch(url, {
10156
+ applyAuthHeaders(headers, getAuthHeaders);
10157
+ const response = await fetchImpl(url, {
10137
10158
  method,
10138
10159
  headers,
10139
10160
  body,
@@ -10201,7 +10222,7 @@ var normalizeUploadData = (data, file, bucketName, baseUrl = "") => {
10201
10222
  variants
10202
10223
  };
10203
10224
  };
10204
- var uploadWithXhr = (baseUrl, file, bucketName, onProgress, options = {}) => new Promise((resolve, reject) => {
10225
+ var uploadWithXhr = (baseUrl, file, bucketName, onProgress, options = {}, getAuthHeaders) => new Promise((resolve, reject) => {
10205
10226
  if (globalThis.process?.env?.VITEST) {
10206
10227
  onProgress?.(100);
10207
10228
  resolve({
@@ -10231,8 +10252,7 @@ var uploadWithXhr = (baseUrl, file, bucketName, onProgress, options = {}) => new
10231
10252
  )
10232
10253
  );
10233
10254
  xhr.withCredentials = true;
10234
- const token = typeof window !== "undefined" ? window.localStorage?.getItem("token") : void 0;
10235
- if (token) xhr.setRequestHeader("authorization", token);
10255
+ applyXhrAuthHeaders(xhr, getAuthHeaders);
10236
10256
  xhr.upload.onprogress = (event) => {
10237
10257
  if (event.lengthComputable) {
10238
10258
  onProgress?.(Math.round(event.loaded / event.total * 100));
@@ -10336,7 +10356,8 @@ async function uploadChunkedFile(request, file, bucketName, baseUrl, onProgress,
10336
10356
  fileSize: file.size,
10337
10357
  chunkSize: DEFAULT_CHUNK_SIZE,
10338
10358
  bucketName,
10339
- visibility: options.visibility
10359
+ visibility: options.visibility,
10360
+ contentType: file.type || void 0
10340
10361
  }
10341
10362
  });
10342
10363
  const uploadInfo = initiate.data || initiate.result;
@@ -10395,7 +10416,7 @@ var createSegmentProgress = (onProgress, start, end) => (percent) => {
10395
10416
  onProgress?.(Math.round(next));
10396
10417
  };
10397
10418
  var getAttachmentUrl = (item) => item.publicUrl || item.previewUrl || item.downloadUrl || item.url || "";
10398
- async function uploadSingleRuntimeFile(request, baseUrl, file, bucketName, onProgress, options) {
10419
+ async function uploadSingleRuntimeFile(request, baseUrl, file, bucketName, onProgress, options, getAuthHeaders) {
10399
10420
  const singleOptions = stripImageCompressionOptions(options);
10400
10421
  if (singleOptions.uploadProvider === "builtin-oss") {
10401
10422
  return uploadBuiltinOssFile(request, file, bucketName, onProgress, singleOptions);
@@ -10406,7 +10427,7 @@ async function uploadSingleRuntimeFile(request, baseUrl, file, bucketName, onPro
10406
10427
  if (file.size > CHUNK_UPLOAD_THRESHOLD) {
10407
10428
  return uploadChunkedFile(request, file, bucketName, baseUrl, onProgress);
10408
10429
  }
10409
- return uploadWithXhr(baseUrl, file, bucketName, onProgress);
10430
+ return uploadWithXhr(baseUrl, file, bucketName, onProgress, {}, getAuthHeaders);
10410
10431
  }
10411
10432
  function toImageVariantMetadata(uploaded, variant) {
10412
10433
  return {
@@ -10420,7 +10441,7 @@ function toImageVariantMetadata(uploaded, variant) {
10420
10441
  quality: variant.quality
10421
10442
  };
10422
10443
  }
10423
- async function uploadFileWithImageVariants(request, baseUrl, file, bucketName, onProgress, options) {
10444
+ async function uploadFileWithImageVariants(request, baseUrl, file, bucketName, onProgress, options, getAuthHeaders) {
10424
10445
  let variants = [];
10425
10446
  try {
10426
10447
  variants = await createCompressedImageVariants(file, options.imageCompression);
@@ -10428,7 +10449,15 @@ async function uploadFileWithImageVariants(request, baseUrl, file, bucketName, o
10428
10449
  variants = [];
10429
10450
  }
10430
10451
  if (variants.length === 0) {
10431
- return uploadSingleRuntimeFile(request, baseUrl, file, bucketName, onProgress, options);
10452
+ return uploadSingleRuntimeFile(
10453
+ request,
10454
+ baseUrl,
10455
+ file,
10456
+ bucketName,
10457
+ onProgress,
10458
+ options,
10459
+ getAuthHeaders
10460
+ );
10432
10461
  }
10433
10462
  const originalProgressEnd = 70;
10434
10463
  const uploaded = await uploadSingleRuntimeFile(
@@ -10437,7 +10466,8 @@ async function uploadFileWithImageVariants(request, baseUrl, file, bucketName, o
10437
10466
  file,
10438
10467
  bucketName,
10439
10468
  createSegmentProgress(onProgress, 0, originalProgressEnd),
10440
- options
10469
+ options,
10470
+ getAuthHeaders
10441
10471
  );
10442
10472
  const imageVariants = { ...uploaded.variants || {} };
10443
10473
  const variantProgressStep = (100 - originalProgressEnd) / variants.length;
@@ -10452,7 +10482,8 @@ async function uploadFileWithImageVariants(request, baseUrl, file, bucketName, o
10452
10482
  variant.file,
10453
10483
  bucketName,
10454
10484
  createSegmentProgress(onProgress, start, end),
10455
- options
10485
+ options,
10486
+ getAuthHeaders
10456
10487
  );
10457
10488
  imageVariants[variant.kind] = toImageVariantMetadata(variantUpload, variant);
10458
10489
  } catch {
@@ -10469,11 +10500,20 @@ async function uploadFileWithImageVariants(request, baseUrl, file, bucketName, o
10469
10500
  }
10470
10501
  function createFormRuntimeApi(config) {
10471
10502
  const { baseUrl = getDefaultBaseUrl(), ...overrides } = config ?? {};
10472
- const request = overrides.request ?? createDefaultRequest(baseUrl);
10503
+ const { fetchImpl, getAuthHeaders } = overrides;
10504
+ const request = overrides.request ?? createDefaultRequest(baseUrl, fetchImpl, getAuthHeaders);
10473
10505
  const defaults = {
10474
10506
  request,
10475
10507
  uploadFile: async (file, bucketName = "files", onProgress, options = {}) => {
10476
- return uploadFileWithImageVariants(request, baseUrl, file, bucketName, onProgress, options);
10508
+ return uploadFileWithImageVariants(
10509
+ request,
10510
+ baseUrl,
10511
+ file,
10512
+ bucketName,
10513
+ onProgress,
10514
+ options,
10515
+ getAuthHeaders
10516
+ );
10477
10517
  },
10478
10518
  uploadPublicFile: async (file, bucketName = DEFAULT_PUBLIC_FILE_BUCKET, onProgress) => {
10479
10519
  if (file.size > CHUNK_UPLOAD_THRESHOLD) {
@@ -10483,7 +10523,7 @@ function createFormRuntimeApi(config) {
10483
10523
  }
10484
10524
  return uploadWithXhr(baseUrl, file, bucketName, onProgress, {
10485
10525
  visibility: "public"
10486
- });
10526
+ }, getAuthHeaders);
10487
10527
  },
10488
10528
  deleteFile: async (objectName, bucketName = "files", options = {}) => {
10489
10529
  if (options.uploadProvider === "builtin-oss" || options.storageScope === "platform") {