@vibe-agent-toolkit/utils 0.1.39-rc.1 → 0.1.39-rc.11

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 (70) hide show
  1. package/dist/fs/file-hash.d.ts +17 -0
  2. package/dist/fs/file-hash.d.ts.map +1 -0
  3. package/dist/fs/file-hash.js +23 -0
  4. package/dist/fs/file-hash.js.map +1 -0
  5. package/dist/git-url.d.ts +43 -0
  6. package/dist/git-url.d.ts.map +1 -0
  7. package/dist/git-url.js +135 -0
  8. package/dist/git-url.js.map +1 -0
  9. package/dist/glob/glob-pattern.d.ts +62 -0
  10. package/dist/glob/glob-pattern.d.ts.map +1 -0
  11. package/dist/glob/glob-pattern.js +115 -0
  12. package/dist/glob/glob-pattern.js.map +1 -0
  13. package/dist/index.d.ts +9 -0
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +12 -0
  16. package/dist/index.js.map +1 -1
  17. package/dist/link-auth/build-headers.d.ts +34 -0
  18. package/dist/link-auth/build-headers.d.ts.map +1 -0
  19. package/dist/link-auth/build-headers.js +58 -0
  20. package/dist/link-auth/build-headers.js.map +1 -0
  21. package/dist/link-auth/expand-macro.d.ts +38 -0
  22. package/dist/link-auth/expand-macro.d.ts.map +1 -0
  23. package/dist/link-auth/expand-macro.js +131 -0
  24. package/dist/link-auth/expand-macro.js.map +1 -0
  25. package/dist/link-auth/macros.yaml +50 -0
  26. package/dist/link-auth/resolve-token.d.ts +49 -0
  27. package/dist/link-auth/resolve-token.d.ts.map +1 -0
  28. package/dist/link-auth/resolve-token.js +72 -0
  29. package/dist/link-auth/resolve-token.js.map +1 -0
  30. package/dist/link-auth/resolve.d.ts +58 -0
  31. package/dist/link-auth/resolve.d.ts.map +1 -0
  32. package/dist/link-auth/resolve.js +55 -0
  33. package/dist/link-auth/resolve.js.map +1 -0
  34. package/dist/link-auth/rewrite.d.ts +52 -0
  35. package/dist/link-auth/rewrite.d.ts.map +1 -0
  36. package/dist/link-auth/rewrite.js +102 -0
  37. package/dist/link-auth/rewrite.js.map +1 -0
  38. package/dist/link-auth/select-provider.d.ts +30 -0
  39. package/dist/link-auth/select-provider.d.ts.map +1 -0
  40. package/dist/link-auth/select-provider.js +55 -0
  41. package/dist/link-auth/select-provider.js.map +1 -0
  42. package/dist/link-auth/template.d.ts +40 -0
  43. package/dist/link-auth/template.d.ts.map +1 -0
  44. package/dist/link-auth/template.js +89 -0
  45. package/dist/link-auth/template.js.map +1 -0
  46. package/dist/link-auth/transforms.d.ts +46 -0
  47. package/dist/link-auth/transforms.d.ts.map +1 -0
  48. package/dist/link-auth/transforms.js +52 -0
  49. package/dist/link-auth/transforms.js.map +1 -0
  50. package/dist/path-utils.d.ts +35 -3
  51. package/dist/path-utils.d.ts.map +1 -1
  52. package/dist/path-utils.js +63 -3
  53. package/dist/path-utils.js.map +1 -1
  54. package/dist/skill-test/auth-resolver.d.ts +34 -0
  55. package/dist/skill-test/auth-resolver.d.ts.map +1 -0
  56. package/dist/skill-test/auth-resolver.js +68 -0
  57. package/dist/skill-test/auth-resolver.js.map +1 -0
  58. package/dist/skill-test/env-scrub.d.ts +61 -0
  59. package/dist/skill-test/env-scrub.d.ts.map +1 -0
  60. package/dist/skill-test/env-scrub.js +112 -0
  61. package/dist/skill-test/env-scrub.js.map +1 -0
  62. package/dist/skill-test/index.d.ts +4 -0
  63. package/dist/skill-test/index.d.ts.map +1 -0
  64. package/dist/skill-test/index.js +4 -0
  65. package/dist/skill-test/index.js.map +1 -0
  66. package/dist/skill-test/spawn-claude.d.ts +45 -0
  67. package/dist/skill-test/spawn-claude.d.ts.map +1 -0
  68. package/dist/skill-test/spawn-claude.js +92 -0
  69. package/dist/skill-test/spawn-claude.js.map +1 -0
  70. package/package.json +4 -3
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Compute the SHA-256 hash of a file's raw bytes.
3
+ *
4
+ * Reads the file synchronously and returns the full lowercase hex digest (64
5
+ * characters). The hash is computed over the raw byte content, so it is stable
6
+ * across platforms for identical file content and changes whenever the content
7
+ * changes.
8
+ *
9
+ * @param path - Absolute (or relative) path to the file to hash.
10
+ * @returns Lowercase hex SHA-256 digest string (64 characters).
11
+ *
12
+ * @example
13
+ * const hash = fileContentHash('/path/to/file.txt');
14
+ * // '2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824'
15
+ */
16
+ export declare function fileContentHash(path: string): string;
17
+ //# sourceMappingURL=file-hash.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-hash.d.ts","sourceRoot":"","sources":["../../src/fs/file-hash.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAIpD"}
@@ -0,0 +1,23 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { readFileSync } from 'node:fs';
3
+ /**
4
+ * Compute the SHA-256 hash of a file's raw bytes.
5
+ *
6
+ * Reads the file synchronously and returns the full lowercase hex digest (64
7
+ * characters). The hash is computed over the raw byte content, so it is stable
8
+ * across platforms for identical file content and changes whenever the content
9
+ * changes.
10
+ *
11
+ * @param path - Absolute (or relative) path to the file to hash.
12
+ * @returns Lowercase hex SHA-256 digest string (64 characters).
13
+ *
14
+ * @example
15
+ * const hash = fileContentHash('/path/to/file.txt');
16
+ * // '2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824'
17
+ */
18
+ export function fileContentHash(path) {
19
+ // eslint-disable-next-line security/detect-non-literal-fs-filename -- path is caller-supplied; callers are responsible for path safety
20
+ const bytes = readFileSync(path);
21
+ return createHash('sha256').update(bytes).digest('hex');
22
+ }
23
+ //# sourceMappingURL=file-hash.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-hash.js","sourceRoot":"","sources":["../../src/fs/file-hash.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,uIAAuI;IACvI,MAAM,KAAK,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IACjC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC1D,CAAC"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Parsed representation of a git URL accepted by `vat audit`.
3
+ *
4
+ * - `cloneUrl` is the URL passed to `git clone` (after stripping ref/subpath
5
+ * fragments and after expanding GitHub shorthand to a full HTTPS URL).
6
+ * - `ref` is an optional branch or tag name (deep commit SHAs are not
7
+ * guaranteed to work with shallow clone — see design spec for details).
8
+ * - `subpath` is an optional subdirectory within the cloned repo to audit.
9
+ */
10
+ export interface ParsedGitUrl {
11
+ cloneUrl: string;
12
+ ref?: string;
13
+ subpath?: string;
14
+ }
15
+ /**
16
+ * Parse a string into a {@link ParsedGitUrl}.
17
+ *
18
+ * Accepted forms:
19
+ * - `https://host/owner/repo.git`
20
+ * - `https://host/owner/repo.git#ref`
21
+ * - `https://host/owner/repo.git#ref:subpath`
22
+ * - `https://github.com/owner/repo/tree/<ref>/<subpath>` (GitHub web URL)
23
+ * - `owner/repo` (GitHub shorthand → expanded to HTTPS)
24
+ * - `git@host:owner/repo.git`
25
+ * - `ssh://git@host/owner/repo.git`
26
+ *
27
+ * Throws on malformed input.
28
+ */
29
+ export declare function parseGitUrl(input: string): ParsedGitUrl;
30
+ /**
31
+ * Detect whether a string should be treated as a git URL (for the polymorphic
32
+ * `[git-url-or-path]` audit argument). True for:
33
+ * - http(s):// URLs
34
+ * - ssh:// URLs
35
+ * - file:// URLs (used by integration tests against local bare repos)
36
+ * - git@host:path scp-style URLs
37
+ * - GitHub shorthand `owner/repo` (strict — no extensions, no extra slashes)
38
+ *
39
+ * Everything else (including relative paths like `./foo/bar`, absolute paths,
40
+ * and multi-segment paths like `foo/bar/baz`) is treated as a filesystem path.
41
+ */
42
+ export declare function isGitUrl(input: string): boolean;
43
+ //# sourceMappingURL=git-url.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git-url.d.ts","sourceRoot":"","sources":["../src/git-url.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAyBD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,CAgEvD;AASD;;;;;;;;;;;GAWG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAmB/C"}
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Split a URL with an optional `#ref[:subpath]` fragment into the base URL
3
+ * and fragment components. Keeps the fragment-handling logic local so the
4
+ * per-form regexes can stay simple and anchored.
5
+ */
6
+ function splitFragment(input) {
7
+ const hashIndex = input.indexOf('#');
8
+ if (hashIndex === -1) {
9
+ return { base: input };
10
+ }
11
+ const base = input.slice(0, hashIndex);
12
+ const fragment = input.slice(hashIndex + 1);
13
+ const colonIndex = fragment.indexOf(':');
14
+ if (colonIndex === -1) {
15
+ return { base, ref: fragment };
16
+ }
17
+ return {
18
+ base,
19
+ ref: fragment.slice(0, colonIndex),
20
+ subpath: fragment.slice(colonIndex + 1),
21
+ };
22
+ }
23
+ /**
24
+ * Parse a string into a {@link ParsedGitUrl}.
25
+ *
26
+ * Accepted forms:
27
+ * - `https://host/owner/repo.git`
28
+ * - `https://host/owner/repo.git#ref`
29
+ * - `https://host/owner/repo.git#ref:subpath`
30
+ * - `https://github.com/owner/repo/tree/<ref>/<subpath>` (GitHub web URL)
31
+ * - `owner/repo` (GitHub shorthand → expanded to HTTPS)
32
+ * - `git@host:owner/repo.git`
33
+ * - `ssh://git@host/owner/repo.git`
34
+ *
35
+ * Throws on malformed input.
36
+ */
37
+ export function parseGitUrl(input) {
38
+ const trimmed = input.trim();
39
+ if (trimmed === '') {
40
+ throw new Error(`Invalid git URL or path: <empty>.`);
41
+ }
42
+ // file:// form: file:///path/to/repo[#ref[:subpath]] — used primarily by
43
+ // integration tests that clone a local bare repo. `git clone` accepts
44
+ // file:// natively.
45
+ if (trimmed.startsWith('file://')) {
46
+ const { base, ref, subpath } = splitFragment(trimmed);
47
+ return buildParsed(base, ref, subpath);
48
+ }
49
+ // HTTPS .git form: https://host/path.git[#ref[:subpath]]
50
+ if (/^https?:\/\//.test(trimmed)) {
51
+ const { base, ref, subpath } = splitFragment(trimmed);
52
+ if (base.endsWith('.git')) {
53
+ return buildParsed(base, ref, subpath);
54
+ }
55
+ // GitHub web URL: https://github.com/owner/repo/tree/<ref>[/<subpath>]
56
+ const ghWeb =
57
+ // eslint-disable-next-line security/detect-unsafe-regex -- Anchored ^...$ with bounded character classes; the only variable-length group is the trailing subpath. Safe from ReDoS.
58
+ /^https:\/\/github\.com\/([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+)\/tree\/([^/]+)(?:\/(.+))?$/.exec(trimmed);
59
+ if (ghWeb) {
60
+ const owner = ghWeb[1] ?? '';
61
+ const repo = ghWeb[2] ?? '';
62
+ const webRef = ghWeb[3];
63
+ const webSubpath = ghWeb[4];
64
+ return buildParsed(`https://github.com/${owner}/${repo}.git`, webRef, webSubpath);
65
+ }
66
+ }
67
+ // SSH ssh:// form: ssh://git@host/path[#ref[:subpath]]
68
+ if (trimmed.startsWith('ssh://')) {
69
+ const { base, ref, subpath } = splitFragment(trimmed);
70
+ return buildParsed(base, ref, subpath);
71
+ }
72
+ // SSH scp-like form: git@host:owner/repo.git[#ref[:subpath]]
73
+ // Anchored, no alternation with nested quantifiers — safe from ReDoS.
74
+ if (/^[^@\s]+@[^:\s]+:[^#\s]+/.test(trimmed)) {
75
+ const { base, ref, subpath } = splitFragment(trimmed);
76
+ return buildParsed(base, ref, subpath);
77
+ }
78
+ // GitHub shorthand: owner/repo[#ref[:subpath]] (single slash in base)
79
+ const { base, ref, subpath } = splitFragment(trimmed);
80
+ const shorthand = /^([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+)$/.exec(base);
81
+ if (shorthand) {
82
+ const owner = shorthand[1] ?? '';
83
+ const repo = shorthand[2] ?? '';
84
+ return buildParsed(`https://github.com/${owner}/${repo}.git`, ref, subpath);
85
+ }
86
+ throw new Error(`Invalid git URL or path: ${input}. Accepted forms: ` +
87
+ `https://<host>/<owner>/<repo>.git, ` +
88
+ `git@<host>:<owner>/<repo>.git, ` +
89
+ `<owner>/<repo>, or a local filesystem path.`);
90
+ }
91
+ function buildParsed(cloneUrl, ref, subpath) {
92
+ const result = { cloneUrl };
93
+ if (ref !== undefined && ref !== '')
94
+ result.ref = ref;
95
+ if (subpath !== undefined && subpath !== '')
96
+ result.subpath = subpath;
97
+ return result;
98
+ }
99
+ /**
100
+ * Detect whether a string should be treated as a git URL (for the polymorphic
101
+ * `[git-url-or-path]` audit argument). True for:
102
+ * - http(s):// URLs
103
+ * - ssh:// URLs
104
+ * - file:// URLs (used by integration tests against local bare repos)
105
+ * - git@host:path scp-style URLs
106
+ * - GitHub shorthand `owner/repo` (strict — no extensions, no extra slashes)
107
+ *
108
+ * Everything else (including relative paths like `./foo/bar`, absolute paths,
109
+ * and multi-segment paths like `foo/bar/baz`) is treated as a filesystem path.
110
+ */
111
+ export function isGitUrl(input) {
112
+ const trimmed = input.trim();
113
+ if (trimmed === '')
114
+ return false;
115
+ if (/^https?:\/\//.test(trimmed))
116
+ return true;
117
+ if (trimmed.startsWith('ssh://'))
118
+ return true;
119
+ if (trimmed.startsWith('file://'))
120
+ return true;
121
+ // Match the same scp-style pattern parseGitUrl uses, including the
122
+ // requirement of a non-empty path segment after the colon. Without the
123
+ // trailing `[^#\s]+`, `isGitUrl` would accept inputs like `foo@host:`
124
+ // that parseGitUrl then rejects with a less-helpful error.
125
+ if (/^[^@\s]+@[^:\s]+:[^#\s]+/.test(trimmed))
126
+ return true;
127
+ // Strict GitHub shorthand: exactly two segments, no extension on second,
128
+ // no path separators beyond the single /. Strip any `#ref[:subpath]`
129
+ // fragment first so `owner/repo#main` and `owner/repo#main:sub` are
130
+ // recognized — `parseGitUrl` handles fragments uniformly across forms.
131
+ const hashIndex = trimmed.indexOf('#');
132
+ const base = hashIndex === -1 ? trimmed : trimmed.slice(0, hashIndex);
133
+ return /^[A-Za-z0-9_-]+\/[A-Za-z0-9_-]+$/.test(base);
134
+ }
135
+ //# sourceMappingURL=git-url.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git-url.js","sourceRoot":"","sources":["../src/git-url.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Pure string helpers for glob pattern analysis.
3
+ *
4
+ * No filesystem access. These are building blocks for higher-level glob
5
+ * expansion utilities. Glob patterns always use forward slashes by convention —
6
+ * this module treats `/` as the only path separator (never `\`).
7
+ *
8
+ * ### Escape semantics
9
+ * A backslash immediately preceding a metachar (`*`, `?`, `[`) neutralises it.
10
+ * Any other backslash is left as-is. The algorithm scans left-to-right and
11
+ * sets an `escaped` flag when it sees `\`; the *next* character is skipped for
12
+ * magic detection if `escaped` is true.
13
+ */
14
+ /**
15
+ * Returns `true` iff `source` contains at least one unescaped glob metachar
16
+ * (`*`, `?`, `[`). A backslash immediately before a metachar escapes it.
17
+ *
18
+ * @example
19
+ * isGlob('a/b/*.mjs') // true — unescaped *
20
+ * isGlob('packs/**\/*') // true — unescaped **
21
+ * isGlob('x?.txt') // true — unescaped ?
22
+ * isGlob('files[1].txt') // true — unescaped [
23
+ * isGlob('foo\\*.txt') // false — * is backslash-escaped
24
+ * isGlob('foo/bar.txt') // false — no metachar
25
+ */
26
+ export declare function isGlob(source: string): boolean;
27
+ /**
28
+ * The longest leading path prefix of `pattern` that contains no glob magic —
29
+ * i.e. the static directory base suitable as a `cwd` for a glob runner.
30
+ *
31
+ * Algorithm:
32
+ * 1. Split pattern on `/` (glob patterns always use forward slashes).
33
+ * 2. Accumulate segments until (and **excluding**) the first magic segment.
34
+ * 3. Join accumulated segments with `/`.
35
+ * 4. If the very first segment is magic → return `'.'`.
36
+ * 5. If NO segment is magic (not a glob at all) → return the whole pattern.
37
+ *
38
+ * Output always uses forward slashes (pure string ops, no `node:path`).
39
+ *
40
+ * @example
41
+ * staticGlobBase('modules/packs/**\/*') // 'modules/packs'
42
+ * staticGlobBase('a/b/*.mjs') // 'a/b'
43
+ * staticGlobBase('*.mjs') // '.'
44
+ * staticGlobBase('../mycli/dist/*.mjs') // '../mycli/dist'
45
+ * staticGlobBase('foo/bar.txt') // 'foo/bar.txt'
46
+ */
47
+ export declare function staticGlobBase(pattern: string): string;
48
+ /**
49
+ * The sub-pattern remaining after stripping the static base, with no leading
50
+ * `/`. This is the pattern to pass to a glob runner with `cwd` set to the base.
51
+ *
52
+ * Only meaningful when `isGlob(pattern)` is true — for a non-glob input the
53
+ * return value is `''` (the whole pattern was consumed as the base).
54
+ *
55
+ * @example
56
+ * globMagicRemainder('modules/packs/**\/*') // '**\/*'
57
+ * globMagicRemainder('a/b/*.mjs') // '*.mjs'
58
+ * globMagicRemainder('*.mjs') // '*.mjs'
59
+ * globMagicRemainder('../mycli/dist/*.mjs') // '*.mjs'
60
+ */
61
+ export declare function globMagicRemainder(pattern: string): string;
62
+ //# sourceMappingURL=glob-pattern.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"glob-pattern.d.ts","sourceRoot":"","sources":["../../src/glob/glob-pattern.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAUH;;;;;;;;;;;GAWG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAgB9C;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAsBtD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAY1D"}
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Pure string helpers for glob pattern analysis.
3
+ *
4
+ * No filesystem access. These are building blocks for higher-level glob
5
+ * expansion utilities. Glob patterns always use forward slashes by convention —
6
+ * this module treats `/` as the only path separator (never `\`).
7
+ *
8
+ * ### Escape semantics
9
+ * A backslash immediately preceding a metachar (`*`, `?`, `[`) neutralises it.
10
+ * Any other backslash is left as-is. The algorithm scans left-to-right and
11
+ * sets an `escaped` flag when it sees `\`; the *next* character is skipped for
12
+ * magic detection if `escaped` is true.
13
+ */
14
+ import { toForwardSlash } from '../path-utils.js';
15
+ /** The glob metacharacters recognised by this module. */
16
+ const MAGIC_CHARS = new Set(['*', '?', '[']);
17
+ /** Sentinel returned by {@link staticGlobBase} when the first segment is magic. */
18
+ const DOT = '.';
19
+ /**
20
+ * Returns `true` iff `source` contains at least one unescaped glob metachar
21
+ * (`*`, `?`, `[`). A backslash immediately before a metachar escapes it.
22
+ *
23
+ * @example
24
+ * isGlob('a/b/*.mjs') // true — unescaped *
25
+ * isGlob('packs/**\/*') // true — unescaped **
26
+ * isGlob('x?.txt') // true — unescaped ?
27
+ * isGlob('files[1].txt') // true — unescaped [
28
+ * isGlob('foo\\*.txt') // false — * is backslash-escaped
29
+ * isGlob('foo/bar.txt') // false — no metachar
30
+ */
31
+ export function isGlob(source) {
32
+ let escaped = false;
33
+ for (const ch of source) {
34
+ if (escaped) {
35
+ escaped = false;
36
+ continue;
37
+ }
38
+ if (ch === '\\') {
39
+ escaped = true;
40
+ continue;
41
+ }
42
+ if (MAGIC_CHARS.has(ch)) {
43
+ return true;
44
+ }
45
+ }
46
+ return false;
47
+ }
48
+ /**
49
+ * The longest leading path prefix of `pattern` that contains no glob magic —
50
+ * i.e. the static directory base suitable as a `cwd` for a glob runner.
51
+ *
52
+ * Algorithm:
53
+ * 1. Split pattern on `/` (glob patterns always use forward slashes).
54
+ * 2. Accumulate segments until (and **excluding**) the first magic segment.
55
+ * 3. Join accumulated segments with `/`.
56
+ * 4. If the very first segment is magic → return `'.'`.
57
+ * 5. If NO segment is magic (not a glob at all) → return the whole pattern.
58
+ *
59
+ * Output always uses forward slashes (pure string ops, no `node:path`).
60
+ *
61
+ * @example
62
+ * staticGlobBase('modules/packs/**\/*') // 'modules/packs'
63
+ * staticGlobBase('a/b/*.mjs') // 'a/b'
64
+ * staticGlobBase('*.mjs') // '.'
65
+ * staticGlobBase('../mycli/dist/*.mjs') // '../mycli/dist'
66
+ * staticGlobBase('foo/bar.txt') // 'foo/bar.txt'
67
+ */
68
+ export function staticGlobBase(pattern) {
69
+ // Glob patterns are always forward-slash; toForwardSlash() satisfies the
70
+ // no-hardcoded-path-split lint rule while being a no-op in practice.
71
+ const normalized = toForwardSlash(pattern);
72
+ const segments = normalized.split('/');
73
+ const staticSegments = [];
74
+ for (const segment of segments) {
75
+ if (isGlob(segment)) {
76
+ break;
77
+ }
78
+ staticSegments.push(segment);
79
+ }
80
+ if (staticSegments.length === 0) {
81
+ return DOT;
82
+ }
83
+ if (staticSegments.length === segments.length) {
84
+ // No magic found — return the whole (normalized) pattern unchanged.
85
+ return normalized;
86
+ }
87
+ return staticSegments.join('/');
88
+ }
89
+ /**
90
+ * The sub-pattern remaining after stripping the static base, with no leading
91
+ * `/`. This is the pattern to pass to a glob runner with `cwd` set to the base.
92
+ *
93
+ * Only meaningful when `isGlob(pattern)` is true — for a non-glob input the
94
+ * return value is `''` (the whole pattern was consumed as the base).
95
+ *
96
+ * @example
97
+ * globMagicRemainder('modules/packs/**\/*') // '**\/*'
98
+ * globMagicRemainder('a/b/*.mjs') // '*.mjs'
99
+ * globMagicRemainder('*.mjs') // '*.mjs'
100
+ * globMagicRemainder('../mycli/dist/*.mjs') // '*.mjs'
101
+ */
102
+ export function globMagicRemainder(pattern) {
103
+ const base = staticGlobBase(pattern);
104
+ if (base === DOT) {
105
+ // First segment was magic — the remainder is the full (forward-slash) pattern.
106
+ return toForwardSlash(pattern);
107
+ }
108
+ if (base === toForwardSlash(pattern)) {
109
+ // No magic at all — degenerate case: remainder is empty.
110
+ return '';
111
+ }
112
+ // Strip the base prefix and the trailing '/' separator.
113
+ return toForwardSlash(pattern).slice(base.length + 1);
114
+ }
115
+ //# sourceMappingURL=glob-pattern.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"glob-pattern.js","sourceRoot":"","sources":["../../src/glob/glob-pattern.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAElD,yDAAyD;AACzD,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;AAE7C,mFAAmF;AACnF,MAAM,GAAG,GAAG,GAAG,CAAC;AAEhB;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,MAAM,CAAC,MAAc;IACnC,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,KAAK,MAAM,EAAE,IAAI,MAAM,EAAE,CAAC;QACxB,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,GAAG,KAAK,CAAC;YAChB,SAAS;QACX,CAAC;QACD,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC;YAChB,OAAO,GAAG,IAAI,CAAC;YACf,SAAS;QACX,CAAC;QACD,IAAI,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;YACxB,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,cAAc,CAAC,OAAe;IAC5C,yEAAyE;IACzE,qEAAqE;IACrE,MAAM,UAAU,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;IAC3C,MAAM,QAAQ,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACvC,MAAM,cAAc,GAAa,EAAE,CAAC;IAEpC,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,IAAI,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;YACpB,MAAM;QACR,CAAC;QACD,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC/B,CAAC;IAED,IAAI,cAAc,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChC,OAAO,GAAG,CAAC;IACb,CAAC;IACD,IAAI,cAAc,CAAC,MAAM,KAAK,QAAQ,CAAC,MAAM,EAAE,CAAC;QAC9C,oEAAoE;QACpE,OAAO,UAAU,CAAC;IACpB,CAAC;IACD,OAAO,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClC,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAAe;IAChD,MAAM,IAAI,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;IACrC,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;QACjB,+EAA+E;QAC/E,OAAO,cAAc,CAAC,OAAO,CAAC,CAAC;IACjC,CAAC;IACD,IAAI,IAAI,KAAK,cAAc,CAAC,OAAO,CAAC,EAAE,CAAC;QACrC,yDAAyD;QACzD,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,wDAAwD;IACxD,OAAO,cAAc,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AACxD,CAAC"}
package/dist/index.d.ts CHANGED
@@ -10,6 +10,7 @@ export * from './asset-reference.js';
10
10
  export * from './fs-utils.js';
11
11
  export * from './file-crawler.js';
12
12
  export * from './gitignore-checker.js';
13
+ export * from './git-url.js';
13
14
  export * from './git-utils.js';
14
15
  export * from './project-utils.js';
15
16
  export * from './git-tracker.js';
@@ -17,4 +18,12 @@ export * from './test-helpers.js';
17
18
  export * from './zod-introspection.js';
18
19
  export * from './template.js';
19
20
  export * from './skill-targets.js';
21
+ export { type LinkAuthConfig, type Provider, type ProviderAuth, type ProviderCheck, resolveAuthenticatedUrl, type ResolveOutcome, } from './link-auth/resolve.js';
22
+ export type { ProviderMatch } from './link-auth/select-provider.js';
23
+ export type { RewriteRule } from './link-auth/rewrite.js';
24
+ export type { TokenSource } from './link-auth/resolve-token.js';
25
+ export { expandMacro, UnknownMacroError } from './link-auth/expand-macro.js';
26
+ export * from './skill-test/index.js';
27
+ export * from './glob/glob-pattern.js';
28
+ export * from './fs/file-hash.js';
20
29
  //# sourceMappingURL=index.d.ts.map
@@ -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,sBAAsB,CAAC;AAGrC,cAAc,eAAe,CAAC;AAG9B,cAAc,mBAAmB,CAAC;AAGlC,cAAc,wBAAwB,CAAC;AAGvC,cAAc,gBAAgB,CAAC;AAI/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,cAAc,CAAC;AAG7B,cAAc,gBAAgB,CAAC;AAI/B,cAAc,oBAAoB,CAAC;AAGnC,cAAc,kBAAkB,CAAC;AAGjC,cAAc,mBAAmB,CAAC;AAGlC,cAAc,wBAAwB,CAAC;AAGvC,cAAc,eAAe,CAAC;AAG9B,cAAc,oBAAoB,CAAC;AAInC,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,QAAQ,EACb,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,uBAAuB,EACvB,KAAK,cAAc,GACpB,MAAM,wBAAwB,CAAC;AAChC,YAAY,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AACpE,YAAY,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAC1D,YAAY,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAG7E,cAAc,uBAAuB,CAAC;AAGtC,cAAc,wBAAwB,CAAC;AAGvC,cAAc,mBAAmB,CAAC"}
package/dist/index.js CHANGED
@@ -16,6 +16,8 @@ export * from './fs-utils.js';
16
16
  export * from './file-crawler.js';
17
17
  // Git ignore checking
18
18
  export * from './gitignore-checker.js';
19
+ // Git URL parsing (parse/detect git URLs, GitHub shorthand, SSH forms)
20
+ export * from './git-url.js';
19
21
  // Git utilities (using git commands directly)
20
22
  export * from './git-utils.js';
21
23
  // Project root discovery (canonical: config → git → null).
@@ -31,4 +33,14 @@ export * from './zod-introspection.js';
31
33
  export * from './template.js';
32
34
  // Skill target resolution (cross-platform flat skill install paths)
33
35
  export * from './skill-targets.js';
36
+ // linkAuth pure engine — public API only (issue #113).
37
+ // Internal helpers (rewrite, build-headers, etc.) stay module-private.
38
+ export { resolveAuthenticatedUrl, } from './link-auth/resolve.js';
39
+ export { expandMacro, UnknownMacroError } from './link-auth/expand-macro.js';
40
+ // Skill testing utilities (environment management for headless agent runs)
41
+ export * from './skill-test/index.js';
42
+ // Glob pattern helpers (isGlob, staticGlobBase, globMagicRemainder)
43
+ export * from './glob/glob-pattern.js';
44
+ // Filesystem hashing (sha256 of raw file bytes)
45
+ export * from './fs/file-hash.js';
34
46
  //# sourceMappingURL=index.js.map
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,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,2DAA2D;AAC3D,iEAAiE;AACjE,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,uEAAuE;AACvE,cAAc,cAAc,CAAC;AAE7B,8CAA8C;AAC9C,cAAc,gBAAgB,CAAC;AAE/B,2DAA2D;AAC3D,iEAAiE;AACjE,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;AAEnC,uDAAuD;AACvD,uEAAuE;AACvE,OAAO,EAKL,uBAAuB,GAExB,MAAM,wBAAwB,CAAC;AAIhC,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAE7E,2EAA2E;AAC3E,cAAc,uBAAuB,CAAC;AAEtC,oEAAoE;AACpE,cAAc,wBAAwB,CAAC;AAEvC,gDAAgD;AAChD,cAAc,mBAAmB,CAAC"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Render auth-header templates with rendered values, and a structural
3
+ * redaction helper for serialization.
4
+ *
5
+ * `buildHeaders` renders each header value template against a context that
6
+ * carries `${token}` plus any named captures / vars from the rewrite step.
7
+ * `redactHeaders` masks `Authorization` values for any caller that needs to
8
+ * serialize headers into logs, errors, or cache entries — the design's §8
9
+ * "tokens never leak" claim depends on every such site routing through this.
10
+ *
11
+ * Per design issue #113 §4 (auth.headers vocabulary) and §8 (redaction is
12
+ * structural; Authorization values never appear in serialized output).
13
+ */
14
+ export declare const REDACTED_VALUE = "<redacted>";
15
+ /**
16
+ * Render a map of header templates into a map of concrete header values.
17
+ *
18
+ * @throws {TemplateMissingVarError} if a header template references an
19
+ * unknown context key
20
+ * @throws {TemplateSyntaxError} from a malformed template expression
21
+ * @throws {UnknownTransformError} from a template calling an unknown transform
22
+ */
23
+ export declare function buildHeaders(templates: Record<string, string>, context: Record<string, string>): Record<string, string>;
24
+ /**
25
+ * Return a copy of `headers` with sensitive values replaced by `REDACTED_VALUE`.
26
+ * Header-name matching is case-insensitive but exact (no prefix matching) — a
27
+ * header like `X-Authorization-Foo` is NOT considered sensitive.
28
+ *
29
+ * **Input must be a plain key-value object.** A `Headers` instance (Web Fetch
30
+ * API) or a `Map` yields `[]` from `Object.entries` and would silently no-op
31
+ * redaction — converting to a plain object is the caller's responsibility.
32
+ */
33
+ export declare function redactHeaders(headers: Record<string, string>): Record<string, string>;
34
+ //# sourceMappingURL=build-headers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-headers.d.ts","sourceRoot":"","sources":["../../src/link-auth/build-headers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,eAAO,MAAM,cAAc,eAAe,CAAC;AAc3C;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAC1B,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACjC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC9B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAMxB;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAMrF"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Render auth-header templates with rendered values, and a structural
3
+ * redaction helper for serialization.
4
+ *
5
+ * `buildHeaders` renders each header value template against a context that
6
+ * carries `${token}` plus any named captures / vars from the rewrite step.
7
+ * `redactHeaders` masks `Authorization` values for any caller that needs to
8
+ * serialize headers into logs, errors, or cache entries — the design's §8
9
+ * "tokens never leak" claim depends on every such site routing through this.
10
+ *
11
+ * Per design issue #113 §4 (auth.headers vocabulary) and §8 (redaction is
12
+ * structural; Authorization values never appear in serialized output).
13
+ */
14
+ import { renderTemplate } from './template.js';
15
+ export const REDACTED_VALUE = '<redacted>';
16
+ /**
17
+ * Header names whose values must be masked when serialized.
18
+ *
19
+ * v1 ships only `authorization` because that is the only secret-bearing
20
+ * header the current macros emit. **Omission is the security risk** — any
21
+ * future macro that emits a header carrying a secret (`Cookie`,
22
+ * `Proxy-Authorization`, `X-API-Key`, custom bearer-style headers) must add
23
+ * that name here, or the token will silently leak through serialization.
24
+ * Extending the set is not the dangerous edit; forgetting to extend it is.
25
+ */
26
+ const SENSITIVE_HEADER_NAMES = new Set(['authorization']);
27
+ /**
28
+ * Render a map of header templates into a map of concrete header values.
29
+ *
30
+ * @throws {TemplateMissingVarError} if a header template references an
31
+ * unknown context key
32
+ * @throws {TemplateSyntaxError} from a malformed template expression
33
+ * @throws {UnknownTransformError} from a template calling an unknown transform
34
+ */
35
+ export function buildHeaders(templates, context) {
36
+ const headers = Object.create(null);
37
+ for (const [name, template] of Object.entries(templates)) {
38
+ headers[name] = renderTemplate(template, context);
39
+ }
40
+ return headers;
41
+ }
42
+ /**
43
+ * Return a copy of `headers` with sensitive values replaced by `REDACTED_VALUE`.
44
+ * Header-name matching is case-insensitive but exact (no prefix matching) — a
45
+ * header like `X-Authorization-Foo` is NOT considered sensitive.
46
+ *
47
+ * **Input must be a plain key-value object.** A `Headers` instance (Web Fetch
48
+ * API) or a `Map` yields `[]` from `Object.entries` and would silently no-op
49
+ * redaction — converting to a plain object is the caller's responsibility.
50
+ */
51
+ export function redactHeaders(headers) {
52
+ const redacted = Object.create(null);
53
+ for (const [name, value] of Object.entries(headers)) {
54
+ redacted[name] = SENSITIVE_HEADER_NAMES.has(name.toLowerCase()) ? REDACTED_VALUE : value;
55
+ }
56
+ return redacted;
57
+ }
58
+ //# sourceMappingURL=build-headers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-headers.js","sourceRoot":"","sources":["../../src/link-auth/build-headers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE/C,MAAM,CAAC,MAAM,cAAc,GAAG,YAAY,CAAC;AAE3C;;;;;;;;;GASG;AACH,MAAM,sBAAsB,GAAwB,IAAI,GAAG,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC;AAE/E;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAC1B,SAAiC,EACjC,OAA+B;IAE/B,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAA2B,CAAC;IAC9D,KAAK,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;QACzD,OAAO,CAAC,IAAI,CAAC,GAAG,cAAc,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IACpD,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,aAAa,CAAC,OAA+B;IAC3D,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAA2B,CAAC;IAC/D,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACpD,QAAQ,CAAC,IAAI,CAAC,GAAG,sBAAsB,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,KAAK,CAAC;IAC3F,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Macro loader + expander.
3
+ *
4
+ * Loads the bundled `macros.yaml` once at module init and exposes
5
+ * `expandMacro(name, overrides?)` which deep-merges the named macro with any
6
+ * adopter overrides. The merge is "adopter wins": objects merge recursively,
7
+ * arrays and primitives are replaced wholesale (no element-wise array merge).
8
+ *
9
+ * The shipped macro file is at `link-auth/macros.yaml`, copied into the dist
10
+ * tree by `packages/dev-tools/src/copy-yaml-assets.ts` during build so the
11
+ * runtime `fs.readFileSync(new URL('./macros.yaml', import.meta.url))`
12
+ * resolves in both source-mode (vitest) and built-mode (dist).
13
+ *
14
+ * Per design issue #113 §5 (macros are config, not a privileged code path).
15
+ */
16
+ /**
17
+ * Thrown when a `use: <name>` references a macro not in the shipped set.
18
+ * Message lists the available macros so a typo surfaces clearly.
19
+ */
20
+ export declare class UnknownMacroError extends Error {
21
+ constructor(name: string, available: readonly string[]);
22
+ }
23
+ /**
24
+ * Look up a macro by name and deep-merge optional adopter overrides on top.
25
+ *
26
+ * Merge semantics:
27
+ * - Plain objects merge recursively (sibling keys preserved).
28
+ * - Arrays are replaced wholesale (override's array wins; no concat).
29
+ * - Primitives are replaced.
30
+ * - `undefined` in an override is treated as "not provided" (base wins).
31
+ *
32
+ * The returned object and all nested plain objects use null prototypes so
33
+ * `__proto__` / `constructor` keys can never poison consumers.
34
+ *
35
+ * @throws {UnknownMacroError} if `name` is not in the shipped macro set
36
+ */
37
+ export declare function expandMacro(name: string, overrides?: Record<string, unknown>): Record<string, unknown>;
38
+ //# sourceMappingURL=expand-macro.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"expand-macro.d.ts","sourceRoot":"","sources":["../../src/link-auth/expand-macro.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAwCH;;;GAGG;AACH,qBAAa,iBAAkB,SAAQ,KAAK;gBAC9B,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,MAAM,EAAE;CAIvD;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,MAAM,EACZ,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAClC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAYzB"}