@deepseek-ai/dsh-util-workspace-path 0.1.3-alpha.2 → 0.1.5-alpha.1

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/README.i18n.yaml CHANGED
@@ -2,5 +2,5 @@
2
2
  # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
3
  # after editing either side, bring the other along and re-record with:
4
4
  # pnpm run verify-translation-pairing --write packages/util/workspace-path/README.md
5
- README.md: 1b09bda74c8836655789c57fa05d03a2892c16b9
6
- README.zh.md: 25128e5fec504e1724f5cc7952fed976985912d5
5
+ README.md: 0b820992dffe0ce52530f45b01fa73ad8f1c9a23
6
+ README.zh.md: 5648d9e37f1fd6bde8a8c0237051c3f801b01a72
package/README.md CHANGED
@@ -9,15 +9,25 @@ English | [中文](README.zh.md)
9
9
 
10
10
  ## Summary
11
11
 
12
- Browser-safe path helpers shared by Workspace-facing client and controller packages. The package joins Workspace-relative paths, abbreviates POSIX home directories for display, and derives Workspace titles from POSIX or Windows paths. It has no Cordis service or runtime state.
12
+ Browser-safe path helpers shared by Workspace-facing client and controller packages. The package joins Workspace-relative paths, abbreviates POSIX home directories for display, derives Workspace titles from POSIX or Windows paths, and owns the `dsh-resource://file/…` address grammar that names a workspace file across the Sidebar and the resource model. It has no Cordis service or runtime state.
13
13
 
14
14
  ## Table of Contents
15
15
 
16
+ - [File addresses](#file-addresses)
16
17
  - [Known Limitations and Deferred Work](#known-limitations-and-deferred-work)
17
18
  - [Dev Note](#dev-note)
18
19
 
19
20
  -----
20
21
 
22
+ <a id="file-addresses"></a>
23
+ ## File addresses
24
+
25
+ A resource address is `dsh-resource://<type>/…`, and the type — the URI host — is the resource protocol key (`file`, or one a plugin declares in `ResourceProtocolMap`); any other scheme is a navigation protocol, defined elsewhere. A file address has one of two scopes. `dsh-resource://file/session/<sessionId>/<path>` names a file by its path relative to that Session's workspace root (`dsh-resource://file/session/abc123/src/notes.txt`), which the Host resolves against the root it holds for the Session. `dsh-resource://file/absolute/<path>` names a file by its absolute path with the leading `/` dropped (`dsh-resource://file/absolute/home/ys/notes.txt` on POSIX, `dsh-resource://file/absolute/C:/x/y.txt` for a Windows drive, `dsh-resource://file/absolute//server/share/y.txt` for a UNC path, whose empty first segment keeps its identity); it carries no Session, so the reader's own Session resolves it, and the Host's workspace confinement still applies. The grammar lives in [`src/file-address.ts`](src/file-address.ts); the path helpers stay in [`src/index.ts`](src/index.ts), which re-exports it.
26
+
27
+ `sessionFileAddress(sessionId, relativePath)` and `absoluteFileAddress(absolutePath)` build one: `\` becomes `/`, a leading `./` or `/` is dropped, and every id and path segment is component-encoded with `:` kept literal, so `#`, `?`, and spaces in a name survive while a drive letter reads as written. `fileAddressFor(sessionId, cwd, path)` chooses the scope for a path as a caller holds it: a relative path, or an absolute path inside `cwd`, becomes `session`-relative; any other absolute path becomes `absolute`. `parseFileAddress(address)` reads one back through `new URL()`: the scheme must be `dsh-resource` and the host exactly `file`; a `session` address yields `{ scope, sessionId, path }` with the workspace-relative path, an `absolute` address yields `{ scope, path }` with the leading `/` restored (`//` for a UNC path) unless the path starts with a drive letter. It returns `undefined` for another type or scheme, an unknown scope, a missing id or path, a non-URL, or a malformed escape — the caller decides whether that is a failure.
28
+
29
+ -----
30
+
21
31
  ## Known Limitations and Deferred Work
22
32
 
23
33
  <a id="known-limitations-and-deferred-work"></a>
package/README.zh.md CHANGED
@@ -9,15 +9,25 @@ kind: "package-library"
9
9
 
10
10
  ## 概述
11
11
 
12
- 供 Workspace 相关客户端和控制器包共享、可在浏览器使用的路径辅助函数。该包负责拼接 Workspace 相对路径、缩写用于展示的 POSIX 主目录,以及从 POSIX 或 Windows 路径提取 Workspace 标题;它不提供 Cordis service,也不持有运行时状态。
12
+ 供 Workspace 相关客户端和控制器包共享、可在浏览器使用的路径辅助函数。该包负责拼接 Workspace 相对路径、缩写用于展示的 POSIX 主目录、从 POSIX 或 Windows 路径提取 Workspace 标题,并拥有在 Sidebar 与资源模型之间命名工作区文件的 `dsh-resource://file/…` 地址语法;它不提供 Cordis service,也不持有运行时状态。
13
13
 
14
14
  ## 目录
15
15
 
16
+ - [文件地址](#file-addresses)
16
17
  - [已知限制与暂缓事项](#known-limitations-and-deferred-work)
17
18
  - [开发备注](#dev-note)
18
19
 
19
20
  -----
20
21
 
22
+ <a id="file-addresses"></a>
23
+ ## 文件地址
24
+
25
+ 资源地址 = `dsh-resource://<type>/…`,type(URI 的 host)即资源协议键(`file`,或插件在 `ResourceProtocolMap` 中声明的键);其他 scheme 属导航协议,另行定义。文件地址有两种作用域。`dsh-resource://file/session/<sessionId>/<path>` 以相对该 Session 工作区根的路径命名文件(`dsh-resource://file/session/abc123/src/notes.txt`),由 Host 对它为该 Session 持有的根解析。`dsh-resource://file/absolute/<path>` 以去掉前导 `/` 的绝对路径命名文件(POSIX 上为 `dsh-resource://file/absolute/home/ys/notes.txt`,Windows 盘符为 `dsh-resource://file/absolute/C:/x/y.txt`,UNC 路径为 `dsh-resource://file/absolute//server/share/y.txt`,其空的首段保留 UNC 身份);它不带 Session,由读者自己的 Session 解析,Host 的工作区限制照样适用。语法住在 [`src/file-address.ts`](src/file-address.ts);路径辅助函数留在 [`src/index.ts`](src/index.ts) 并再导出它。
26
+
27
+ `sessionFileAddress(sessionId, relativePath)` 与 `absoluteFileAddress(absolutePath)` 构造地址:`\` 归一为 `/`,去掉前导 `./` 或 `/`,id 与每个路径段做组件编码但 `:` 保持字面,因此名字里的 `#`、`?`、空格都能保留,盘符也照原样可读。`fileAddressFor(sessionId, cwd, path)` 按调用方手里的路径选作用域:相对路径或落在 `cwd` 内的绝对路径成为 `session` 相对地址,其他绝对路径成为 `absolute` 地址。`parseFileAddress(address)` 用 `new URL()` 读回:scheme 必须是 `dsh-resource`、host 必须恰为 `file`;`session` 地址得到 `{ scope, sessionId, path }`(path 为工作区相对路径),`absolute` 地址得到 `{ scope, path }` 并还原前导 `/`(UNC 路径还原为 `//`),以盘符开头者除外。对其他 type 或 scheme、未知作用域、缺 id 或路径、非 URL、或转义格式错误的输入返回 `undefined`,是否算失败由调用方决定。
28
+
29
+ -----
30
+
21
31
  ## 已知限制与暂缓事项
22
32
 
23
33
  <a id="known-limitations-and-deferred-work"></a>
package/lib/index.js CHANGED
@@ -1,3 +1,84 @@
1
+ //#region lib/types/file-address.js
2
+ /**
3
+ * The `dsh-resource://file/…` address grammar: how a file is named across the
4
+ * Sidebar and the resource model, built and parsed without touching a
5
+ * filesystem.
6
+ * @module
7
+ */
8
+ /** The scheme and type every file address opens with. */
9
+ const FILE_ADDRESS_PREFIX = "dsh-resource://file/";
10
+ /** Component-encode one id or path segment, keeping `:` literal for drive letters. */
11
+ function encodeSegment(segment) {
12
+ return encodeURIComponent(segment).replace(/%3A/gi, ":");
13
+ }
14
+ /** Encode a `/`-separated path segment by segment. */
15
+ function encodePath(path) {
16
+ return path.split("/").map(encodeSegment).join("/");
17
+ }
18
+ /** Whether a decoded first path segment is a Windows drive (`C:`). */
19
+ function isDriveSegment(segment) {
20
+ return segment !== void 0 && /^[A-Za-z]:$/.test(segment);
21
+ }
22
+ /**
23
+ * Build the address of a file inside one Session's workspace.
24
+ * @param sessionId - the Session whose workspace root the path is relative to.
25
+ * @param path - workspace-relative path; backslashes are normalized to `/`, and a leading `./` or `/` is dropped.
26
+ * @returns the `dsh-resource://file/session/<sessionId>/<path>` address.
27
+ */
28
+ function sessionFileAddress(sessionId, path) {
29
+ const relative = path.replace(/\\/g, "/").replace(/^(?:\.\/)+/, "").replace(/^\/+/, "");
30
+ return `${FILE_ADDRESS_PREFIX}session/${encodeSegment(sessionId)}/${encodePath(relative)}`;
31
+ }
32
+ /**
33
+ * Build the address of a file by its absolute path.
34
+ * @param path - absolute path; backslashes are normalized to `/` and the leading `/` is dropped,
35
+ * except that a UNC path (`\\server\share`) keeps one empty first segment.
36
+ * @returns the `dsh-resource://file/absolute/<path>` address.
37
+ */
38
+ function absoluteFileAddress(path) {
39
+ const normalized = path.replace(/\\/g, "/");
40
+ const unc = normalized.startsWith("//");
41
+ const absolute = normalized.replace(/^\/+/, "");
42
+ return `${FILE_ADDRESS_PREFIX}absolute/${unc ? "/" : ""}${encodePath(absolute)}`;
43
+ }
44
+ /**
45
+ * Read a file address back into its parts.
46
+ * @param address - a candidate address.
47
+ * @returns the parts, or `undefined` when the string is not a `dsh-resource://file/` URI in a known scope with a path, or a segment is not validly encoded.
48
+ */
49
+ function parseFileAddress(address) {
50
+ try {
51
+ const url = new URL(address);
52
+ if (url.protocol !== "dsh-resource:" || url.host !== "file") return void 0;
53
+ const [, scope, ...rest] = url.pathname.split("/");
54
+ if (scope === "session") {
55
+ const [id, ...segments] = rest;
56
+ if (id === void 0 || id === "" || segments.length === 0) return void 0;
57
+ return {
58
+ scope,
59
+ sessionId: decodeURIComponent(id),
60
+ path: segments.map(decodeURIComponent).join("/")
61
+ };
62
+ }
63
+ if (scope === "absolute") {
64
+ const unc = rest[0] === "" && rest.length > 1;
65
+ const segments = (unc ? rest.slice(1) : rest).map(decodeURIComponent);
66
+ if (segments.length === 0 || segments[0] === "") return void 0;
67
+ if (unc) return {
68
+ scope,
69
+ path: `//${segments.join("/")}`
70
+ };
71
+ return {
72
+ scope,
73
+ path: isDriveSegment(segments[0]) ? segments.join("/") : `/${segments.join("/")}`
74
+ };
75
+ }
76
+ return;
77
+ } catch {
78
+ return;
79
+ }
80
+ }
81
+ //#endregion
1
82
  //#region lib/types/index.js
2
83
  /**
3
84
  * Browser-safe Workspace path and display helpers.
@@ -8,13 +89,21 @@ function isWindowsStylePath(value) {
8
89
  return /^[A-Za-z]:[/\\]/.test(value) || value.startsWith("\\\\");
9
90
  }
10
91
  /**
92
+ * Whether a path is absolute in either spelling the Host accepts: POSIX (`/a/b`) or Windows drive or UNC.
93
+ * @param path - the path to classify.
94
+ * @returns `true` for an absolute path; `false` for a Workspace-relative one.
95
+ */
96
+ function isAbsoluteWorkspacePath(path) {
97
+ return path.startsWith("/") || isWindowsStylePath(path);
98
+ }
99
+ /**
11
100
  * Resolve a Workspace-relative path into the Host-facing spelling used by path operations.
12
101
  * @param cwd - Session Workspace root, when known.
13
102
  * @param path - Absolute or Workspace-relative path.
14
103
  * @returns an absolute path when a Workspace root is available, otherwise the original path.
15
104
  */
16
105
  function resolveWorkspacePath(cwd, path) {
17
- if (path.startsWith("/") || isWindowsStylePath(path)) return path;
106
+ if (isAbsoluteWorkspacePath(path)) return path;
18
107
  if (cwd === void 0 || cwd === "") return path;
19
108
  const separator = isWindowsStylePath(cwd) && cwd.includes("\\") ? "\\" : "/";
20
109
  return `${cwd.replace(/[/\\]+$/, "")}${separator}${path.replace(/^[/\\]+/, "")}`;
@@ -45,5 +134,23 @@ function workspaceTitleOf(path) {
45
134
  const separator = Math.max(trimmed.lastIndexOf("/"), trimmed.lastIndexOf("\\"));
46
135
  return trimmed.slice(separator + 1);
47
136
  }
137
+ /**
138
+ * The address for a path as a caller holds it: a relative path, or an absolute
139
+ * path inside the Session's workspace, becomes a `session`-scoped address; an
140
+ * absolute path outside it, or one whose workspace root is unknown, becomes an
141
+ * `absolute`-scoped address.
142
+ * @param sessionId - the Session the path is read in.
143
+ * @param cwd - that Session's workspace root, when known.
144
+ * @param path - absolute or workspace-relative path, in either separator spelling.
145
+ * @returns the `dsh-resource://file/…` address.
146
+ */
147
+ function fileAddressFor(sessionId, cwd, path) {
148
+ const normalized = path.replace(/\\/g, "/");
149
+ if (!isAbsoluteWorkspacePath(normalized)) return sessionFileAddress(sessionId, normalized);
150
+ const root = cwd === void 0 ? "" : cwd.replace(/\\/g, "/").replace(/\/+$/, "");
151
+ if (root !== "" && normalized === root) return sessionFileAddress(sessionId, "");
152
+ if (root !== "" && normalized.startsWith(`${root}/`)) return sessionFileAddress(sessionId, normalized.slice(root.length + 1));
153
+ return absoluteFileAddress(normalized);
154
+ }
48
155
  //#endregion
49
- export { abbreviateHomePath, resolveWorkspacePath, workspaceTitleOf };
156
+ export { abbreviateHomePath, absoluteFileAddress, fileAddressFor, isAbsoluteWorkspacePath, parseFileAddress, resolveWorkspacePath, sessionFileAddress, workspaceTitleOf };
@@ -0,0 +1,58 @@
1
+ /**
2
+ * The `dsh-resource://file/…` address grammar: how a file is named across the
3
+ * Sidebar and the resource model, built and parsed without touching a
4
+ * filesystem.
5
+ * @module
6
+ */
7
+ /**
8
+ * A file resource address, in one of two scopes.
9
+ *
10
+ * Every resource address is `dsh-resource://<type>/…`, the URI host naming the
11
+ * resource protocol; for `file` the path opens with the scope:
12
+ *
13
+ * - `dsh-resource://file/session/<sessionId>/<path>` names a file by its path
14
+ * relative to that Session's workspace root (`src/a.ts`, no leading `/`); the
15
+ * Host resolves it against the root it holds for the Session.
16
+ * - `dsh-resource://file/absolute/<path>` names a file by its absolute path with
17
+ * the leading `/` dropped (`dsh-resource://file/absolute/home/ys/notes.txt`;
18
+ * Windows `dsh-resource://file/absolute/C:/x/y.txt`; a UNC path keeps an empty
19
+ * first segment, `dsh-resource://file/absolute//server/share/x.txt`). It carries
20
+ * no Session: the reader's own Session resolves it, and the Host's workspace
21
+ * confinement still applies.
22
+ *
23
+ * Every id and path segment is component-encoded, so a name carrying `#`, `?`,
24
+ * or a space survives the round trip; `:` stays literal so a drive letter reads
25
+ * as written.
26
+ */
27
+ export type FileAddress = {
28
+ readonly scope: 'session';
29
+ /** The Session whose workspace root the path is relative to. */
30
+ readonly sessionId: string;
31
+ /** Workspace-relative `/`-separated path, no leading `/`; empty for the root itself. */
32
+ readonly path: string;
33
+ } | {
34
+ readonly scope: 'absolute';
35
+ /** Absolute `/`-separated path: `/a/b` on POSIX, `C:/a/b` for a Windows drive, `//server/share/a` for a UNC path. */
36
+ readonly path: string;
37
+ };
38
+ /**
39
+ * Build the address of a file inside one Session's workspace.
40
+ * @param sessionId - the Session whose workspace root the path is relative to.
41
+ * @param path - workspace-relative path; backslashes are normalized to `/`, and a leading `./` or `/` is dropped.
42
+ * @returns the `dsh-resource://file/session/<sessionId>/<path>` address.
43
+ */
44
+ export declare function sessionFileAddress(sessionId: string, path: string): string;
45
+ /**
46
+ * Build the address of a file by its absolute path.
47
+ * @param path - absolute path; backslashes are normalized to `/` and the leading `/` is dropped,
48
+ * except that a UNC path (`\\server\share`) keeps one empty first segment.
49
+ * @returns the `dsh-resource://file/absolute/<path>` address.
50
+ */
51
+ export declare function absoluteFileAddress(path: string): string;
52
+ /**
53
+ * Read a file address back into its parts.
54
+ * @param address - a candidate address.
55
+ * @returns the parts, or `undefined` when the string is not a `dsh-resource://file/` URI in a known scope with a path, or a segment is not validly encoded.
56
+ */
57
+ export declare function parseFileAddress(address: string): FileAddress | undefined;
58
+ //# sourceMappingURL=file-address.d.ts.map
@@ -1,7 +1,9 @@
1
1
  /**
2
- * Browser-safe Workspace path and display helpers.
3
- * @module @deepseek-ai/dsh-util-workspace-path
2
+ * Whether a path is absolute in either spelling the Host accepts: POSIX (`/a/b`) or Windows drive or UNC.
3
+ * @param path - the path to classify.
4
+ * @returns `true` for an absolute path; `false` for a Workspace-relative one.
4
5
  */
6
+ export declare function isAbsoluteWorkspacePath(path: string): boolean;
5
7
  /**
6
8
  * Resolve a Workspace-relative path into the Host-facing spelling used by path operations.
7
9
  * @param cwd - Session Workspace root, when known.
@@ -23,4 +25,16 @@ export declare function abbreviateHomePath(path: string, home?: string): string;
23
25
  * @returns the final segment, or an empty string for a separator-only path.
24
26
  */
25
27
  export declare function workspaceTitleOf(path: string): string;
28
+ export * from './file-address.ts';
29
+ /**
30
+ * The address for a path as a caller holds it: a relative path, or an absolute
31
+ * path inside the Session's workspace, becomes a `session`-scoped address; an
32
+ * absolute path outside it, or one whose workspace root is unknown, becomes an
33
+ * `absolute`-scoped address.
34
+ * @param sessionId - the Session the path is read in.
35
+ * @param cwd - that Session's workspace root, when known.
36
+ * @param path - absolute or workspace-relative path, in either separator spelling.
37
+ * @returns the `dsh-resource://file/…` address.
38
+ */
39
+ export declare function fileAddressFor(sessionId: string, cwd: string | undefined, path: string): string;
26
40
  //# sourceMappingURL=index.d.ts.map
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@deepseek-ai/dsh-util-workspace-path",
3
3
  "description": "Browser-safe Workspace path and display helpers",
4
- "version": "0.1.3-alpha.2",
4
+ "version": "0.1.5-alpha.1",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },