@burdenoff/fe-libs 2026.1028.8 → 2026.1028.10

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.
@@ -0,0 +1,15 @@
1
+ export type AttachmentAccess = 'allowed' | 'denied' | 'unknown';
2
+ /** Exact operations needed by the Send-time Files commit; no role-name shortcut. */
3
+ export declare const ATTACHMENT_WRITE_PERMISSIONS: readonly [{
4
+ readonly resourceType: "upload-session";
5
+ readonly action: "create";
6
+ }, {
7
+ readonly resourceType: "folder";
8
+ readonly action: "create";
9
+ }, {
10
+ readonly resourceType: "upload-session";
11
+ readonly action: "update";
12
+ }];
13
+ export declare const ATTACHMENT_ACCESS_QUERY = "query AssistantAttachmentPermissions($inputs: [PermissionCheckInput!]!) {\n checkPermissions(inputs: $inputs) { allowed }\n}";
14
+ /** Advisory preflight. An unreadable answer leaves the decision to the real server call. */
15
+ export declare function probeAttachmentAccess(workspaceId: string, actorId: string | null, isSuperAdmin?: boolean): Promise<AttachmentAccess>;
@@ -0,0 +1,42 @@
1
+ import { graphqlFetch as e } from "../graphql/fetch.js";
2
+ import "../../shared-graphql.js";
3
+ //#region src/shared/assistant/attachmentAccess.ts
4
+ var t = [
5
+ {
6
+ resourceType: "upload-session",
7
+ action: "create"
8
+ },
9
+ {
10
+ resourceType: "folder",
11
+ action: "create"
12
+ },
13
+ {
14
+ resourceType: "upload-session",
15
+ action: "update"
16
+ }
17
+ ], n = "query AssistantAttachmentPermissions($inputs: [PermissionCheckInput!]!) {\n checkPermissions(inputs: $inputs) { allowed }\n}";
18
+ async function r(r, i, a = !1) {
19
+ if (a) return "allowed";
20
+ if (!r || !i) return "unknown";
21
+ try {
22
+ let a = await e({
23
+ gateway: "workspace",
24
+ workspaceId: r,
25
+ query: n,
26
+ variables: { inputs: t.map((e) => ({
27
+ actorId: i,
28
+ actorType: "USER",
29
+ scopeId: r,
30
+ ...e
31
+ })) },
32
+ suppressGlobalErrorEvent: !0
33
+ });
34
+ if (a.errors?.length) return "unknown";
35
+ let o = a.data?.checkPermissions;
36
+ return !Array.isArray(o) || o.length !== t.length || o.some((e) => typeof e?.allowed != "boolean") ? "unknown" : o.every((e) => e.allowed) ? "allowed" : "denied";
37
+ } catch {
38
+ return "unknown";
39
+ }
40
+ }
41
+ //#endregion
42
+ export { n as ATTACHMENT_ACCESS_QUERY, t as ATTACHMENT_WRITE_PERMISSIONS, r as probeAttachmentAccess };
@@ -0,0 +1,9 @@
1
+ import { AssistantTr } from './i18n';
2
+ export declare const ATTACHMENT_EDITOR_ACCESS_COPY: {
3
+ readonly key: "attachments.editorAccessRequired";
4
+ readonly fallback: "You need Editor access in this workspace to attach files.";
5
+ };
6
+ /** AI-written, 2026-10-28 (BOFF-9912); native review pending. No seed/apply performed. */
7
+ export declare const ATTACHMENT_EDITOR_ACCESS_TRANSLATIONS: Readonly<Record<string, string>>;
8
+ /** Server translations win; the six bundled values also work before a product seed. */
9
+ export declare function attachmentEditorAccessMessage(tr: AssistantTr, locale: string): string;
@@ -0,0 +1,18 @@
1
+ //#region src/shared/assistant/attachmentAccessCopy.ts
2
+ var e = {
3
+ key: "attachments.editorAccessRequired",
4
+ fallback: "You need Editor access in this workspace to attach files."
5
+ }, t = {
6
+ en: e.fallback,
7
+ hi: "फ़ाइलें संलग्न करने के लिए आपको इस कार्यक्षेत्र में संपादक की अनुमति चाहिए।",
8
+ ta: "கோப்புகளை இணைக்க, இந்தப் பணியிடத்தில் உங்களுக்கு எடிட்டர் அணுகல் தேவை.",
9
+ es: "Necesitas acceso de Editor en este espacio de trabajo para adjuntar archivos.",
10
+ ar: "تحتاج إلى صلاحية محرّر في مساحة العمل هذه لإرفاق الملفات.",
11
+ zh: "你需要此工作区的编辑者权限才能附加文件。"
12
+ };
13
+ function n(n, r) {
14
+ let i = t[r.toLowerCase().split("-")[0] ?? "en"] ?? e.fallback;
15
+ return n(e.key, i);
16
+ }
17
+ //#endregion
18
+ export { e as ATTACHMENT_EDITOR_ACCESS_COPY, t as ATTACHMENT_EDITOR_ACCESS_TRANSLATIONS, n as attachmentEditorAccessMessage };
@@ -1,5 +1,6 @@
1
1
  import { FilesPort } from '../../storage/filesPort';
2
2
  import { AssistantAttachment } from './attachments';
3
+ import { AttachmentAccess } from './attachmentAccess';
3
4
  /** files-svc switches to MULTIPART above this size; the browser cannot upload those. */
4
5
  export declare const ATTACHMENT_DIRECT_UPLOAD_MAX_BYTES: number;
5
6
  /**
@@ -38,6 +39,7 @@ export type AttachmentCommitPolicy = {
38
39
  };
39
40
  /** Codes for failures the commit decides itself; a server failure keeps the server's own code. */
40
41
  export declare const ATTACHMENT_COMMIT_CODES: {
42
+ readonly editorAccessRequired: "ATTACHMENT_EDITOR_ACCESS_REQUIRED";
41
43
  readonly quotaNotProvisioned: "QUOTA_NOT_PROVISIONED";
42
44
  readonly quotaExceeded: "QUOTA_EXCEEDED";
43
45
  readonly tooLarge: "ATTACHMENT_TOO_LARGE_FOR_DIRECT_UPLOAD";
@@ -46,6 +48,7 @@ export declare const ATTACHMENT_COMMIT_CODES: {
46
48
  readonly timeout: "ATTACHMENT_SAVE_TIMEOUT";
47
49
  readonly uploadFailed: "ATTACHMENT_UPLOAD_FAILED";
48
50
  readonly folderFailed: "ATTACHMENT_FOLDER_FAILED";
51
+ readonly pendingUpload: "ATTACHMENT_UPLOAD_UNRESOLVED";
49
52
  };
50
53
  /** One draft attachment and the bytes held for it in memory. */
51
54
  export interface CommitDraft {
@@ -61,18 +64,22 @@ export interface AttachmentCommitFailure {
61
64
  }
62
65
  /** What a failed rollback leaves for the next commit (or `discardPending`) to retry. */
63
66
  export type PendingDiscard =
64
- /** A file this commit created and could not delete. */
67
+ /** Legacy entry: already completed, retained in Files without a delete request. */
65
68
  {
66
69
  kind: 'file';
67
70
  fileId: string;
68
71
  }
69
- /** A session whose `completeUpload` was issued and whose outcome is unknown: reconcile, delete if FINALIZED. */
72
+ /** A complete with an unknown outcome; retain its File if it FINALIZED. */
70
73
  | {
71
74
  kind: 'session';
72
75
  sessionId: string;
76
+ attachmentId?: string;
73
77
  };
78
+ type SavedAttachment = Pick<AssistantAttachment, 'fileId' | 'downloadUrl' | 'urlResolvedAt'>;
74
79
  export interface PendingDiscardStore {
75
80
  entries: PendingDiscard[];
81
+ /** In-memory retry references only. Never persisted across identities or page reloads. */
82
+ retained?: Map<string, SavedAttachment>;
76
83
  /**
77
84
  * The drain that is running now. A drain takes the entries out of `entries` while it works and
78
85
  * puts back what it could not settle, so a second drain (or the final one after an unmount)
@@ -81,7 +88,7 @@ export interface PendingDiscardStore {
81
88
  draining?: Promise<void>;
82
89
  }
83
90
  export type CommitMode =
84
- /** Any failure undoes everything and sends nothing (composer). */
91
+ /** Any failure cancels the send and keeps completed private files for retry (composer). */
85
92
  'all-or-nothing'
86
93
  /** A failed file is dropped and the rest is kept (voice: the spoken question is still asked). */
87
94
  | 'best-effort';
@@ -93,7 +100,7 @@ export type AttachmentCommitSuccess = {
93
100
  dropped: AttachmentCommitFailure[];
94
101
  /** Call once the files are referenced by a sent message; rollback is a no-op from then on. */
95
102
  handedOver: () => void;
96
- /** Hard-delete what this commit created (the turn could not be sent after all). */
103
+ /** Cancel the send handover; completed Files remain available for retry. Never deletes files. */
97
104
  rollback: () => Promise<void>;
98
105
  };
99
106
  export type AttachmentCommitFailureResult = {
@@ -107,6 +114,8 @@ export type AttachmentCommitFailureResult = {
107
114
  export type AttachmentCommitResult = AttachmentCommitSuccess | AttachmentCommitFailureResult;
108
115
  export interface CommitOptions {
109
116
  port: FilesPort;
117
+ /** Read-only permission check before staging; unknown/error preserves the server decision. */
118
+ preflightAccess?: () => Promise<AttachmentAccess>;
110
119
  /** The product's attachments folder, e.g. `/BigConsole/AI Assistant/Attachments`. */
111
120
  folderPath: string;
112
121
  drafts: readonly CommitDraft[];
@@ -123,13 +132,16 @@ export interface CommitOptions {
123
132
  now?: () => number;
124
133
  sleep?: (ms: number, signal?: AbortSignal) => Promise<void>;
125
134
  }
126
- /** Retry earlier failed rollbacks. A session with a FINALIZED outcome becomes a file delete. */
135
+ /** Reconcile earlier ambiguous completes without deleting a finalized file. */
127
136
  export declare function drainPendingDiscards(port: FilesPort, store: PendingDiscardStore, options?: Pick<CommitOptions, 'sleep'> & {
128
137
  policy?: Partial<AttachmentCommitPolicy>;
129
138
  }): Promise<void>;
130
139
  export declare function commitAttachments(options: CommitOptions): Promise<AttachmentCommitResult>;
131
140
  export interface AttachmentCommitterConfig {
132
141
  port: FilesPort;
142
+ preflightAccess?: CommitOptions['preflightAccess'];
143
+ /** Scope retry state to the identity/workspace used by the Files port. */
144
+ getScopeKey?: () => string;
133
145
  folderPath: string;
134
146
  onChange?: CommitOptions['onChange'];
135
147
  policy?: CommitOptions['policy'];
@@ -172,3 +184,4 @@ export declare function attachmentUrlIsStale(attachment: Pick<AssistantAttachmen
172
184
  * and metadata are intact.
173
185
  */
174
186
  export declare function refreshStaleAttachmentUrls(attachments: readonly AssistantAttachment[], resolveUrl: (fileId: string) => Promise<string | null>, now?: number): Promise<AssistantAttachment[]>;
187
+ export {};