@sparkelf/dsh-knowledge-base 0.1.3 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/client.js +3198 -3001
- package/lib/client.js.map +1 -1
- package/lib/index.js +19 -1
- package/lib/types/client/dropTargetPath.d.ts +33 -0
- package/lib/types/client/locales.d.ts +3 -0
- package/lib/types/client/progressRules.d.ts +17 -17
- package/lib/types/trash.d.ts +14 -0
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -2794,6 +2794,20 @@ const BUILT_IN_ROLES = [{
|
|
|
2794
2794
|
* file keeps its inode, every other caller keeps their link, and restoring is
|
|
2795
2795
|
* deleting the note.
|
|
2796
2796
|
*
|
|
2797
|
+
* ### DO NOT "fix" this by moving or deleting the file
|
|
2798
|
+
*
|
|
2799
|
+
* The obvious-looking change -- make `trash.remove` rename the file into a trash
|
|
2800
|
+
* directory, or unlink it -- is WRONG here, and it is the change a future reader is
|
|
2801
|
+
* most likely to make. A view entry is a hardlink to the shared authoritative copy:
|
|
2802
|
+
* unlinking it removes one link and leaves the data, which looks harmless, but the
|
|
2803
|
+
* next `syncView` finds a standing grant with no link and restores it (see above).
|
|
2804
|
+
* Moving the file is worse: the authoritative path is the one DataOps grants are
|
|
2805
|
+
* written against, so moving it silently invalidates every other caller's grant and
|
|
2806
|
+
* makes the document unreachable for everyone, not just the caller who removed it.
|
|
2807
|
+
*
|
|
2808
|
+
* Removal is a per-caller filter, never a filesystem operation. That is the whole
|
|
2809
|
+
* design, and it is why the trash record exists at all.
|
|
2810
|
+
*
|
|
2797
2811
|
* ## What a tombstone is scoped to
|
|
2798
2812
|
*
|
|
2799
2813
|
* A tombstone belongs to a CALLER, not to a path: two people sharing a document may
|
|
@@ -3192,7 +3206,11 @@ async function syncCallerView(host, config, paths) {
|
|
|
3192
3206
|
};
|
|
3193
3207
|
const trash = await readTrash(viewPathFor(viewOptions));
|
|
3194
3208
|
if (trash.kind === "unreadable") host.logger?.warn?.(`knowledge-base: unusable trash record for ${caller.userId}: ${trash.reason}`);
|
|
3195
|
-
const hidden = trash.kind === "ready" ? trash.entries.
|
|
3209
|
+
const hidden = trash.kind === "ready" ? trash.entries.flatMap((entry) => {
|
|
3210
|
+
const placement = bucketOf(entry.relPath, usableDepartmentPath(caller.departmentPathCodes));
|
|
3211
|
+
const viewPath = placement === void 0 ? void 0 : placement.bucket + "/" + placement.inner;
|
|
3212
|
+
return viewPath === void 0 || viewPath === entry.relPath ? [entry.relPath] : [entry.relPath, viewPath];
|
|
3213
|
+
}) : [];
|
|
3196
3214
|
const trustResolvedSet = !(paths === void 0 && candidates.length > 0 && allowed.length === 0);
|
|
3197
3215
|
const delta = await syncView({
|
|
3198
3216
|
...viewOptions,
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a dropped file actually lands.
|
|
3
|
+
*
|
|
4
|
+
* The tree shows a reader a `personal` folder, but the authoritative tree stores every
|
|
5
|
+
* caller's documents under their own id (`personal/<owner>/<file>`) -- the owner segment
|
|
6
|
+
* is hidden from the view on purpose, because it is an implementation detail a reader
|
|
7
|
+
* never typed. The consequence is that a tree row labelled 「个人」 carries the path
|
|
8
|
+
* `personal`, and writing straight to it puts the file in `personal/` beside the owner
|
|
9
|
+
* directories, where no view will ever show it: the reader uploads, the progress row says
|
|
10
|
+
* "done", and the file is invisible.
|
|
11
|
+
*
|
|
12
|
+
* This module owns that translation so every drop entry point (the borrowed explorer, the
|
|
13
|
+
* plugin's own listing, the Home table) answers the same question the same way.
|
|
14
|
+
*
|
|
15
|
+
* @module @sparkelf/dsh-knowledge-base/client
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Resolve a view-relative directory into the authoritative path a write must use.
|
|
19
|
+
*
|
|
20
|
+
* A `personal` path gains the caller's id as its second segment, at whatever depth the
|
|
21
|
+
* drop names: the bucket row (`personal`), a folder inside it (`personal/specs`), or one
|
|
22
|
+
* nested deeper all resolve to the caller's own subtree. Everything else (`global`,
|
|
23
|
+
* `department/...`, `''` for the mount root) is already authoritative and passes through.
|
|
24
|
+
*
|
|
25
|
+
* A `personal` path with no caller id is REFUSED rather than passed through. Writing to
|
|
26
|
+
* `personal` without an owner is exactly the defect this function exists to prevent, so
|
|
27
|
+
* the refusal is explicit: `undefined` means "no safe destination", and the caller reports
|
|
28
|
+
* it instead of writing somewhere the reader cannot see.
|
|
29
|
+
* @param viewRelDir - the directory as the VIEW names it (what a tree row carries).
|
|
30
|
+
* @param callerId - the id the Host reports for the caller, when it is known.
|
|
31
|
+
* @returns The authoritative relative directory, or undefined when it cannot be resolved.
|
|
32
|
+
*/
|
|
33
|
+
export declare function resolveUploadDir(viewRelDir: string, callerId: string | undefined): string | undefined;
|
|
@@ -342,6 +342,9 @@ export declare const en: {
|
|
|
342
342
|
readonly 'step.sshfs-mount': "Mount the share over SSHFS";
|
|
343
343
|
readonly 'step.sshfs-verify': "Verify the SSHFS mount";
|
|
344
344
|
readonly deployLocalOnly: "Deployment steps run on a remote host. This workspace stores documents locally, so there is nothing to deploy.";
|
|
345
|
+
readonly dropRefusedNoCaller: "Cannot place this upload";
|
|
346
|
+
readonly dropRefusedNoCallerReason: "The host has not reported your account yet, so the personal folder cannot be resolved. Reload and try again.";
|
|
347
|
+
readonly progressResize: "Drag to resize";
|
|
345
348
|
readonly shareEmpty: "Nothing has been shared yet.";
|
|
346
349
|
readonly shareGroupReceived: "Inbound";
|
|
347
350
|
readonly shareGroupGiven: "Outbound";
|
|
@@ -9,23 +9,6 @@
|
|
|
9
9
|
*
|
|
10
10
|
* @module @sparkelf/dsh-knowledge-base/client
|
|
11
11
|
*/
|
|
12
|
-
/**
|
|
13
|
-
* Whether the strip may fold now.
|
|
14
|
-
*
|
|
15
|
-
* Folded means "one summary line instead of a list". It folds when every task in both
|
|
16
|
-
* groups reached a terminal state AND the batch has not been folded before -- the
|
|
17
|
-
* second half is what stops it re-folding after a reader expanded a finished batch to
|
|
18
|
-
* read which file failed.
|
|
19
|
-
* @param uploads - the current batch's rows.
|
|
20
|
-
* @param extractions - the extraction tasks the Host reports.
|
|
21
|
-
* @param alreadyFolded - whether a fold already happened for this batch.
|
|
22
|
-
* @returns Whether to fold.
|
|
23
|
-
*/
|
|
24
|
-
export declare function shouldFoldStrip(uploads: readonly {
|
|
25
|
-
state: string;
|
|
26
|
-
}[], extractions: readonly {
|
|
27
|
-
state: string;
|
|
28
|
-
}[], alreadyFolded: boolean): boolean;
|
|
29
12
|
/**
|
|
30
13
|
* Whether a retry button would help for this state.
|
|
31
14
|
*
|
|
@@ -52,3 +35,20 @@ export declare function failureReason(body: {
|
|
|
52
35
|
error?: unknown;
|
|
53
36
|
code?: unknown;
|
|
54
37
|
} | undefined, status: number): string;
|
|
38
|
+
/** The strip's height bounds, in pixels. */
|
|
39
|
+
export declare const PROGRESS_MIN_HEIGHT = 44;
|
|
40
|
+
export declare const PROGRESS_DEFAULT_HEIGHT = 180;
|
|
41
|
+
/** The share of the panel's height the strip may take, leaving the tree the rest. */
|
|
42
|
+
export declare const PROGRESS_MAX_SHARE = 0.6;
|
|
43
|
+
/**
|
|
44
|
+
* Clamp a dragged height into the strip's legal range.
|
|
45
|
+
*
|
|
46
|
+
* The upper bound is a share of the PANEL, not a constant: the strip must never grow
|
|
47
|
+
* until the file tree is unreachable, and how much room that is depends on how tall the
|
|
48
|
+
* panel currently is. A 600px panel and a 1200px panel therefore allow different
|
|
49
|
+
* maximums, which is what keeps the tree visible on both.
|
|
50
|
+
* @param desired - the height the pointer asked for.
|
|
51
|
+
* @param panelHeight - the panel's own height, when known.
|
|
52
|
+
* @returns The height to use.
|
|
53
|
+
*/
|
|
54
|
+
export declare function clampProgressHeight(desired: number, panelHeight: number | undefined): number;
|
package/lib/types/trash.d.ts
CHANGED
|
@@ -15,6 +15,20 @@
|
|
|
15
15
|
* file keeps its inode, every other caller keeps their link, and restoring is
|
|
16
16
|
* deleting the note.
|
|
17
17
|
*
|
|
18
|
+
* ### DO NOT "fix" this by moving or deleting the file
|
|
19
|
+
*
|
|
20
|
+
* The obvious-looking change -- make `trash.remove` rename the file into a trash
|
|
21
|
+
* directory, or unlink it -- is WRONG here, and it is the change a future reader is
|
|
22
|
+
* most likely to make. A view entry is a hardlink to the shared authoritative copy:
|
|
23
|
+
* unlinking it removes one link and leaves the data, which looks harmless, but the
|
|
24
|
+
* next `syncView` finds a standing grant with no link and restores it (see above).
|
|
25
|
+
* Moving the file is worse: the authoritative path is the one DataOps grants are
|
|
26
|
+
* written against, so moving it silently invalidates every other caller's grant and
|
|
27
|
+
* makes the document unreachable for everyone, not just the caller who removed it.
|
|
28
|
+
*
|
|
29
|
+
* Removal is a per-caller filter, never a filesystem operation. That is the whole
|
|
30
|
+
* design, and it is why the trash record exists at all.
|
|
31
|
+
*
|
|
18
32
|
* ## What a tombstone is scoped to
|
|
19
33
|
*
|
|
20
34
|
* A tombstone belongs to a CALLER, not to a path: two people sharing a document may
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sparkelf/dsh-knowledge-base",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "DataOps-backed knowledge base for DeepSeek Harness: a left-sidebar destination, Settings configuration, and index tooling over a mounted NAS view.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|