@vibe-agent-toolkit/utils 0.1.35 → 0.1.37

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,35 @@
1
+ /**
2
+ * Resolve a VAT "asset reference" to an absolute filesystem path.
3
+ *
4
+ * An asset reference is either:
5
+ * - A filesystem path (relative to baseDir, or absolute), OR
6
+ * - An npm bare specifier (`@scope/pkg/subpath` or `pkg/subpath`),
7
+ * resolved via Node module resolution from baseDir, honoring the
8
+ * target package's `exports` map.
9
+ *
10
+ * Bare specifiers let VAT consumers reference schemas (and future
11
+ * config-supplied files) published as npm packages without hardcoding
12
+ * the package's internal layout. The publisher's `exports` field owns
13
+ * the layout; consumers stay portable.
14
+ *
15
+ * NOTE: this is a VAT-internal abstraction for locating files. It is NOT
16
+ * an RFC 3986 URI reference and is intentionally NOT used by markdown link
17
+ * walkers (including the `format: "uri-reference"` frontmatter checker) —
18
+ * bare specifiers are not valid URIs and would not resolve in a renderer.
19
+ *
20
+ * @example
21
+ * resolveAssetReference('@scope/pkg/schemas/foo.json', '/proj')
22
+ * // -> '/proj/node_modules/@scope/pkg/dist/schemas/foo.json' (per the
23
+ * // package's exports map)
24
+ * resolveAssetReference('./schemas/foo.json', '/proj')
25
+ * // -> '/proj/schemas/foo.json'
26
+ * resolveAssetReference('/abs/foo.json', '/proj')
27
+ * // -> '/abs/foo.json'
28
+ *
29
+ * @param specifier - The asset reference (path or bare npm specifier)
30
+ * @param baseDir - Absolute directory used as the resolution anchor
31
+ * @returns Absolute filesystem path to the asset
32
+ * @throws Error with actionable message and `cause` on resolution failure
33
+ */
34
+ export declare function resolveAssetReference(specifier: string, baseDir: string): string;
35
+ //# sourceMappingURL=asset-reference.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"asset-reference.d.ts","sourceRoot":"","sources":["../src/asset-reference.ts"],"names":[],"mappings":"AAYA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAmBhF"}
@@ -0,0 +1,138 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { createRequire } from 'node:module';
3
+ import { pathToFileURL } from 'node:url';
4
+ import { isAbsolutePath, safePath } from './path-utils.js';
5
+ // First segment must be a valid npm package name (scoped or unscoped),
6
+ // followed by `/` and at least one subpath segment. Paths starting with
7
+ // `.`, `/`, or a Windows drive letter are filesystem paths, never bare
8
+ // specifiers.
9
+ const BARE_SPECIFIER_RE = /^(?:@[^/]+\/[^/]+|[a-z0-9][a-z0-9._-]*)\/.+/i;
10
+ /**
11
+ * Resolve a VAT "asset reference" to an absolute filesystem path.
12
+ *
13
+ * An asset reference is either:
14
+ * - A filesystem path (relative to baseDir, or absolute), OR
15
+ * - An npm bare specifier (`@scope/pkg/subpath` or `pkg/subpath`),
16
+ * resolved via Node module resolution from baseDir, honoring the
17
+ * target package's `exports` map.
18
+ *
19
+ * Bare specifiers let VAT consumers reference schemas (and future
20
+ * config-supplied files) published as npm packages without hardcoding
21
+ * the package's internal layout. The publisher's `exports` field owns
22
+ * the layout; consumers stay portable.
23
+ *
24
+ * NOTE: this is a VAT-internal abstraction for locating files. It is NOT
25
+ * an RFC 3986 URI reference and is intentionally NOT used by markdown link
26
+ * walkers (including the `format: "uri-reference"` frontmatter checker) —
27
+ * bare specifiers are not valid URIs and would not resolve in a renderer.
28
+ *
29
+ * @example
30
+ * resolveAssetReference('@scope/pkg/schemas/foo.json', '/proj')
31
+ * // -> '/proj/node_modules/@scope/pkg/dist/schemas/foo.json' (per the
32
+ * // package's exports map)
33
+ * resolveAssetReference('./schemas/foo.json', '/proj')
34
+ * // -> '/proj/schemas/foo.json'
35
+ * resolveAssetReference('/abs/foo.json', '/proj')
36
+ * // -> '/abs/foo.json'
37
+ *
38
+ * @param specifier - The asset reference (path or bare npm specifier)
39
+ * @param baseDir - Absolute directory used as the resolution anchor
40
+ * @returns Absolute filesystem path to the asset
41
+ * @throws Error with actionable message and `cause` on resolution failure
42
+ */
43
+ export function resolveAssetReference(specifier, baseDir) {
44
+ if (!isBareSpecifier(specifier)) {
45
+ return safePath.resolve(baseDir, specifier);
46
+ }
47
+ const requireFn = createRequire(pathToFileURL(safePath.join(baseDir, 'package.json')).href);
48
+ try {
49
+ return requireFn.resolve(specifier);
50
+ }
51
+ catch (cause) {
52
+ // Unscoped bare specifiers can also be interpreted as relative paths
53
+ // (e.g., `dir/file.json` with no installed package "dir"). Fall back
54
+ // to path resolution. Scoped (`@scope/...`) has no such ambiguity —
55
+ // surface the error.
56
+ if (!specifier.startsWith('@') && isModuleNotFound(cause)) {
57
+ return safePath.resolve(baseDir, specifier);
58
+ }
59
+ throw new Error(formatActionableError(specifier, baseDir, cause), { cause: cause });
60
+ }
61
+ }
62
+ /**
63
+ * Build a message that distinguishes the three common failure modes so callers
64
+ * know where to look. Node's raw error often points at the resolved on-disk
65
+ * path, which adopters easily misread as a VAT path-handling bug.
66
+ */
67
+ function formatActionableError(specifier, baseDir, cause) {
68
+ const headline = formatResolutionError(cause);
69
+ const code = cause?.code;
70
+ const missingPath = extractMissingModulePath(cause);
71
+ // Mode 1: Node walked the package's `exports` map, computed an absolute
72
+ // target path, and that file is not on disk. This is the most confusing
73
+ // case for adopters: the package IS installed, the exports map IS correct,
74
+ // but a build step in the target package didn't run (or produced different
75
+ // output). Name the missing file explicitly and point at the publisher.
76
+ if (code === 'MODULE_NOT_FOUND' && missingPath && missingPath !== specifier && isAbsolutePath(missingPath)) {
77
+ const fileExists = safeExistsSync(missingPath);
78
+ if (!fileExists) {
79
+ return (`Failed to resolve asset reference '${specifier}': ` +
80
+ `the package's "exports" map points to '${missingPath}', but that file does not exist on disk.\n` +
81
+ `Hint: the target package was found, but a build step did not produce this file. ` +
82
+ `Rebuild the publishing package (e.g., \`pnpm --filter <package> build\`) to generate the missing artifact, ` +
83
+ `or verify the package's "exports" subpath pattern matches what its build emits.\n` +
84
+ `Node error: ${headline}`);
85
+ }
86
+ }
87
+ // Mode 2: Exports map didn't expose the requested subpath at all.
88
+ if (code === 'ERR_PACKAGE_PATH_NOT_EXPORTED') {
89
+ return (`Failed to resolve asset reference '${specifier}': ` +
90
+ `the target package does not expose this subpath in its "exports" map.\n` +
91
+ `Hint: check the package's package.json "exports" field — only paths declared there are reachable via bare specifier.\n` +
92
+ `Node error: ${headline}`);
93
+ }
94
+ // Mode 3: Package itself not installed / not reachable from baseDir.
95
+ return (`Failed to resolve asset reference '${specifier}': ${headline}\n` +
96
+ `Check the package's "exports" field, or run install in ${baseDir}.`);
97
+ }
98
+ /**
99
+ * Node's MODULE_NOT_FOUND message has the form: `Cannot find module 'X'`.
100
+ * Pull `X` out so we can decide whether the error refers to the original
101
+ * specifier (package not installed) or to a resolved on-disk path (file
102
+ * missing at exports target).
103
+ */
104
+ const CANNOT_FIND_MODULE_RE = /Cannot find module '([^']+)'/;
105
+ function extractMissingModulePath(cause) {
106
+ if (!(cause instanceof Error))
107
+ return undefined;
108
+ const match = CANNOT_FIND_MODULE_RE.exec(cause.message);
109
+ return match?.[1];
110
+ }
111
+ function safeExistsSync(filePath) {
112
+ try {
113
+ // eslint-disable-next-line security/detect-non-literal-fs-filename -- filePath is a Node-resolved exports target, used only to refine the error message
114
+ return existsSync(filePath);
115
+ }
116
+ catch {
117
+ return false;
118
+ }
119
+ }
120
+ function isBareSpecifier(value) {
121
+ return BARE_SPECIFIER_RE.test(value);
122
+ }
123
+ function isModuleNotFound(err) {
124
+ return (typeof err === 'object' &&
125
+ err !== null &&
126
+ 'code' in err &&
127
+ err.code === 'MODULE_NOT_FOUND');
128
+ }
129
+ function formatResolutionError(err) {
130
+ if (err instanceof Error) {
131
+ // Node's MODULE_NOT_FOUND / ERR_PACKAGE_PATH_NOT_EXPORTED messages are
132
+ // long and noisy; first line is the actionable summary.
133
+ const firstLine = err.message.split('\n', 1)[0];
134
+ return firstLine ?? err.message;
135
+ }
136
+ return String(err);
137
+ }
138
+ //# sourceMappingURL=asset-reference.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"asset-reference.js","sourceRoot":"","sources":["../src/asset-reference.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -6,6 +6,7 @@
6
6
  */
7
7
  export * from './safe-exec.js';
8
8
  export * from './path-utils.js';
9
+ export * from './asset-reference.js';
9
10
  export * from './fs-utils.js';
10
11
  export * from './file-crawler.js';
11
12
  export * from './gitignore-checker.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,cAAc,gBAAgB,CAAC;AAG/B,cAAc,iBAAiB,CAAC;AAGhC,cAAc,eAAe,CAAC;AAG9B,cAAc,mBAAmB,CAAC;AAGlC,cAAc,wBAAwB,CAAC;AAGvC,cAAc,gBAAgB,CAAC;AAG/B,cAAc,oBAAoB,CAAC;AAGnC,cAAc,kBAAkB,CAAC;AAGjC,cAAc,mBAAmB,CAAC;AAGlC,cAAc,wBAAwB,CAAC;AAGvC,cAAc,eAAe,CAAC;AAG9B,cAAc,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,cAAc,gBAAgB,CAAC;AAG/B,cAAc,iBAAiB,CAAC;AAGhC,cAAc,sBAAsB,CAAC;AAGrC,cAAc,eAAe,CAAC;AAG9B,cAAc,mBAAmB,CAAC;AAGlC,cAAc,wBAAwB,CAAC;AAGvC,cAAc,gBAAgB,CAAC;AAG/B,cAAc,oBAAoB,CAAC;AAGnC,cAAc,kBAAkB,CAAC;AAGjC,cAAc,mBAAmB,CAAC;AAGlC,cAAc,wBAAwB,CAAC;AAGvC,cAAc,eAAe,CAAC;AAG9B,cAAc,oBAAoB,CAAC"}
package/dist/index.js CHANGED
@@ -8,6 +8,8 @@
8
8
  export * from './safe-exec.js';
9
9
  // Cross-platform path utilities
10
10
  export * from './path-utils.js';
11
+ // Asset reference resolution (paths + npm bare specifiers)
12
+ export * from './asset-reference.js';
11
13
  // Filesystem utilities
12
14
  export * from './fs-utils.js';
13
15
  // Directory crawling with glob patterns
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,8DAA8D;AAC9D,cAAc,gBAAgB,CAAC;AAE/B,gCAAgC;AAChC,cAAc,iBAAiB,CAAC;AAEhC,uBAAuB;AACvB,cAAc,eAAe,CAAC;AAE9B,wCAAwC;AACxC,cAAc,mBAAmB,CAAC;AAElC,sBAAsB;AACtB,cAAc,wBAAwB,CAAC;AAEvC,8CAA8C;AAC9C,cAAc,gBAAgB,CAAC;AAE/B,kEAAkE;AAClE,cAAc,oBAAoB,CAAC;AAEnC,yDAAyD;AACzD,cAAc,kBAAkB,CAAC;AAEjC,oDAAoD;AACpD,cAAc,mBAAmB,CAAC;AAElC,4CAA4C;AAC5C,cAAc,wBAAwB,CAAC;AAEvC,2DAA2D;AAC3D,cAAc,eAAe,CAAC;AAE9B,oEAAoE;AACpE,cAAc,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,8DAA8D;AAC9D,cAAc,gBAAgB,CAAC;AAE/B,gCAAgC;AAChC,cAAc,iBAAiB,CAAC;AAEhC,2DAA2D;AAC3D,cAAc,sBAAsB,CAAC;AAErC,uBAAuB;AACvB,cAAc,eAAe,CAAC;AAE9B,wCAAwC;AACxC,cAAc,mBAAmB,CAAC;AAElC,sBAAsB;AACtB,cAAc,wBAAwB,CAAC;AAEvC,8CAA8C;AAC9C,cAAc,gBAAgB,CAAC;AAE/B,kEAAkE;AAClE,cAAc,oBAAoB,CAAC;AAEnC,yDAAyD;AACzD,cAAc,kBAAkB,CAAC;AAEjC,oDAAoD;AACpD,cAAc,mBAAmB,CAAC;AAElC,4CAA4C;AAC5C,cAAc,wBAAwB,CAAC;AAEvC,2DAA2D;AAC3D,cAAc,eAAe,CAAC;AAE9B,oEAAoE;AACpE,cAAc,oBAAoB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibe-agent-toolkit/utils",
3
- "version": "0.1.35",
3
+ "version": "0.1.37",
4
4
  "type": "module",
5
5
  "description": "Core utility functions with no external dependencies",
6
6
  "keywords": [