@vibe-agent-toolkit/utils 0.2.0-rc.1 → 0.2.0-rc.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.
- package/README.md +69 -23
- package/dist/asset-reference.js +3 -11
- package/dist/asset-reference.js.map +1 -1
- package/dist/compare-code-units.d.ts +28 -0
- package/dist/compare-code-units.d.ts.map +1 -0
- package/dist/compare-code-units.js +33 -0
- package/dist/compare-code-units.js.map +1 -0
- package/dist/crawl-timing.d.ts +657 -0
- package/dist/crawl-timing.d.ts.map +1 -0
- package/dist/crawl-timing.js +765 -0
- package/dist/crawl-timing.js.map +1 -0
- package/dist/crawl.d.ts +2 -4
- package/dist/crawl.d.ts.map +1 -1
- package/dist/crawl.js +2 -4
- package/dist/crawl.js.map +1 -1
- package/dist/dirent-kind.d.ts +83 -0
- package/dist/dirent-kind.d.ts.map +1 -0
- package/dist/dirent-kind.js +129 -0
- package/dist/dirent-kind.js.map +1 -0
- package/dist/entrypoint.d.ts +54 -0
- package/dist/entrypoint.d.ts.map +1 -0
- package/dist/entrypoint.js +71 -0
- package/dist/entrypoint.js.map +1 -0
- package/dist/env-flag.d.ts +51 -0
- package/dist/env-flag.d.ts.map +1 -0
- package/dist/env-flag.js +62 -0
- package/dist/env-flag.js.map +1 -0
- package/dist/errors/errno.d.ts +51 -0
- package/dist/errors/errno.d.ts.map +1 -0
- package/dist/errors/errno.js +112 -0
- package/dist/errors/errno.js.map +1 -0
- package/dist/errors/vat-error.d.ts +60 -0
- package/dist/errors/vat-error.d.ts.map +1 -0
- package/dist/errors/vat-error.js +86 -0
- package/dist/errors/vat-error.js.map +1 -0
- package/dist/file-crawler.d.ts +60 -0
- package/dist/file-crawler.d.ts.map +1 -1
- package/dist/file-crawler.js +133 -29
- package/dist/file-crawler.js.map +1 -1
- package/dist/fs/file-hash.d.ts.map +1 -1
- package/dist/fs/file-hash.js +0 -1
- package/dist/fs/file-hash.js.map +1 -1
- package/dist/fs-utils.d.ts +422 -191
- package/dist/fs-utils.d.ts.map +1 -1
- package/dist/fs-utils.js +534 -204
- package/dist/fs-utils.js.map +1 -1
- package/dist/fs.d.ts +5 -2
- package/dist/fs.d.ts.map +1 -1
- package/dist/fs.js +9 -7
- package/dist/fs.js.map +1 -1
- package/dist/git-run.d.ts +167 -0
- package/dist/git-run.d.ts.map +1 -0
- package/dist/git-run.js +154 -0
- package/dist/git-run.js.map +1 -0
- package/dist/git-snapshot.d.ts +199 -0
- package/dist/git-snapshot.d.ts.map +1 -0
- package/dist/git-snapshot.js +250 -0
- package/dist/git-snapshot.js.map +1 -0
- package/dist/git-tracker.d.ts +166 -3
- package/dist/git-tracker.d.ts.map +1 -1
- package/dist/git-tracker.js +250 -22
- package/dist/git-tracker.js.map +1 -1
- package/dist/git-utils.d.ts +84 -1
- package/dist/git-utils.d.ts.map +1 -1
- package/dist/git-utils.js +208 -82
- package/dist/git-utils.js.map +1 -1
- package/dist/git.d.ts +13 -4
- package/dist/git.d.ts.map +1 -1
- package/dist/git.js +12 -4
- package/dist/git.js.map +1 -1
- package/dist/gitignore-checker.d.ts.map +1 -1
- package/dist/gitignore-checker.js +15 -6
- package/dist/gitignore-checker.js.map +1 -1
- package/dist/glob/glob-pattern.js +5 -5
- package/dist/glob/glob-pattern.js.map +1 -1
- package/dist/index.d.ts +32 -20
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +95 -50
- package/dist/index.js.map +1 -1
- package/dist/listing-refusal.d.ts +112 -0
- package/dist/listing-refusal.d.ts.map +1 -0
- package/dist/listing-refusal.js +105 -0
- package/dist/listing-refusal.js.map +1 -0
- package/dist/path-containment.d.ts +64 -0
- package/dist/path-containment.d.ts.map +1 -0
- package/dist/path-containment.js +111 -0
- package/dist/path-containment.js.map +1 -0
- package/dist/path-core.d.ts +113 -18
- package/dist/path-core.d.ts.map +1 -1
- package/dist/path-core.js +139 -23
- package/dist/path-core.js.map +1 -1
- package/dist/path-utils.d.ts.map +1 -1
- package/dist/path-utils.js +42 -47
- package/dist/path-utils.js.map +1 -1
- package/dist/path.d.ts +1 -1
- package/dist/path.d.ts.map +1 -1
- package/dist/path.js +1 -1
- package/dist/path.js.map +1 -1
- package/dist/process.d.ts +7 -0
- package/dist/process.d.ts.map +1 -1
- package/dist/process.js +7 -0
- package/dist/process.js.map +1 -1
- package/dist/project-utils.d.ts.map +1 -1
- package/dist/project-utils.js +15 -9
- package/dist/project-utils.js.map +1 -1
- package/dist/project.d.ts +7 -5
- package/dist/project.d.ts.map +1 -1
- package/dist/project.js +7 -5
- package/dist/project.js.map +1 -1
- package/dist/safe-exec.d.ts +40 -11
- package/dist/safe-exec.d.ts.map +1 -1
- package/dist/safe-exec.js +111 -25
- package/dist/safe-exec.js.map +1 -1
- package/dist/skill-targets.d.ts +2 -2
- package/dist/skill-targets.js +2 -2
- package/dist/skill-test/auth-resolver.d.ts +10 -2
- package/dist/skill-test/auth-resolver.d.ts.map +1 -1
- package/dist/skill-test/auth-resolver.js +26 -11
- package/dist/skill-test/auth-resolver.js.map +1 -1
- package/dist/skill-test/index.d.ts +1 -1
- package/dist/skill-test/index.d.ts.map +1 -1
- package/dist/skill-test/index.js +1 -1
- package/dist/skill-test/index.js.map +1 -1
- package/dist/skill-test/spawn-claude.d.ts +30 -1
- package/dist/skill-test/spawn-claude.d.ts.map +1 -1
- package/dist/skill-test/spawn-claude.js +71 -5
- package/dist/skill-test/spawn-claude.js.map +1 -1
- package/dist/skill-test/transcript.d.ts +41 -2
- package/dist/skill-test/transcript.d.ts.map +1 -1
- package/dist/skill-test/transcript.js +55 -10
- package/dist/skill-test/transcript.js.map +1 -1
- package/dist/spawn-hardened.js +0 -1
- package/dist/spawn-hardened.js.map +1 -1
- package/dist/stdio-blocking.js +13 -14
- package/dist/stdio-blocking.js.map +1 -1
- package/dist/test-helpers.d.ts +135 -100
- package/dist/test-helpers.d.ts.map +1 -1
- package/dist/test-helpers.js +224 -146
- package/dist/test-helpers.js.map +1 -1
- package/dist/testing/executables.d.ts +32 -0
- package/dist/testing/executables.d.ts.map +1 -0
- package/dist/testing/executables.js +70 -0
- package/dist/testing/executables.js.map +1 -0
- package/dist/testing/hostile-tree.d.ts +79 -0
- package/dist/testing/hostile-tree.d.ts.map +1 -0
- package/dist/testing/hostile-tree.js +164 -0
- package/dist/testing/hostile-tree.js.map +1 -0
- package/dist/testing/platform-gates.d.ts +31 -0
- package/dist/testing/platform-gates.d.ts.map +1 -0
- package/dist/testing/platform-gates.js +31 -0
- package/dist/testing/platform-gates.js.map +1 -0
- package/dist/testing/temp-dir.d.ts +255 -0
- package/dist/testing/temp-dir.d.ts.map +1 -0
- package/dist/testing/temp-dir.js +363 -0
- package/dist/testing/temp-dir.js.map +1 -0
- package/dist/testing.d.ts +94 -3
- package/dist/testing.d.ts.map +1 -1
- package/dist/testing.js +119 -3
- package/dist/testing.js.map +1 -1
- package/dist/text-content.d.ts +197 -0
- package/dist/text-content.d.ts.map +1 -0
- package/dist/text-content.js +354 -0
- package/dist/text-content.js.map +1 -0
- package/dist/text-file.d.ts +45 -0
- package/dist/text-file.d.ts.map +1 -0
- package/dist/text-file.js +51 -0
- package/dist/text-file.js.map +1 -0
- package/dist/text.d.ts +16 -0
- package/dist/text.d.ts.map +1 -0
- package/dist/text.js +16 -0
- package/dist/text.js.map +1 -0
- package/dist/timing-dump.d.ts +136 -0
- package/dist/timing-dump.d.ts.map +1 -0
- package/dist/timing-dump.js +229 -0
- package/dist/timing-dump.js.map +1 -0
- package/dist/windows-shell.d.ts +0 -1
- package/dist/windows-shell.d.ts.map +1 -1
- package/dist/windows-shell.js +0 -1
- package/dist/windows-shell.js.map +1 -1
- package/dist/zod-introspection.js +2 -2
- package/dist/zod-introspection.js.map +1 -1
- package/eslint/README.md +223 -49
- package/eslint/index.cjs +111 -97
- package/eslint/index.d.cts +16 -6
- package/eslint/rules/callee-name.cjs +23 -0
- package/eslint/rules/commands-import-boundary.cjs +148 -0
- package/eslint/rules/dead-import.cjs +1 -1
- package/eslint/rules/dirent-type-needs-symlink-check.cjs +282 -0
- package/eslint/rules/eslint-rule-factory.cjs +5 -2
- package/eslint/rules/exempt-path-matcher.cjs +2 -1
- package/eslint/rules/explicit-zod-strictness.cjs +120 -0
- package/eslint/rules/no-bare-dynamic-import-path.cjs +5 -1
- package/eslint/rules/no-bare-executable-spawn.cjs +70 -0
- package/eslint/rules/no-bare-symlink-in-tests.cjs +213 -0
- package/eslint/rules/no-blind-catch.cjs +171 -0
- package/eslint/rules/no-child-process-execSync.cjs +8 -0
- package/eslint/rules/no-decaying-referent.cjs +217 -0
- package/eslint/rules/no-dotdot-containment.cjs +160 -0
- package/eslint/rules/no-file-url-string-concat.cjs +4 -1
- package/eslint/rules/no-fragile-entrypoint-guard.cjs +228 -0
- package/eslint/rules/no-fs-mkdirSync.cjs +8 -0
- package/eslint/rules/no-fs-promises-cp.cjs +7 -0
- package/eslint/rules/no-fs-realpathSync.cjs +8 -0
- package/eslint/rules/no-hardcoded-path-split.cjs +29 -18
- package/eslint/rules/no-io-in-unit-tier.cjs +129 -0
- package/eslint/rules/no-literal-process-exit.cjs +129 -0
- package/eslint/rules/no-manual-path-normalize.cjs +136 -107
- package/eslint/rules/no-os-tmpdir.cjs +8 -0
- package/eslint/rules/no-path-operations-in-comparisons.cjs +7 -2
- package/eslint/rules/no-path-sep-in-strings.cjs +7 -2
- package/eslint/rules/no-path-startswith.cjs +7 -2
- package/eslint/rules/no-process-exit-in-phase.cjs +127 -0
- package/eslint/rules/no-raw-node-path.cjs +563 -0
- package/eslint/rules/no-raw-text-decode.cjs +305 -0
- package/eslint/rules/no-registry-count-pin.cjs +255 -0
- package/eslint/rules/no-self-package-import.cjs +168 -0
- package/eslint/rules/no-test-scoped-functions.cjs +10 -3
- package/eslint/rules/no-unix-shell-commands.cjs +6 -2
- package/eslint/rules/no-unsafe-root-join.cjs +23 -2
- package/eslint/rules/no-url-pathname-for-fs.cjs +5 -1
- package/eslint/rules/no-version-literal.cjs +151 -0
- package/eslint/rules/prefer-startswith-over-regex.cjs +9 -0
- package/eslint/rules/require-justified-skip.cjs +8 -2
- package/eslint/rules/safe-import.cjs +1 -1
- package/package.json +16 -16
- package/dist/link-auth/build-headers.d.ts +0 -34
- package/dist/link-auth/build-headers.d.ts.map +0 -1
- package/dist/link-auth/build-headers.js +0 -58
- package/dist/link-auth/build-headers.js.map +0 -1
- package/dist/link-auth/expand-macro.d.ts +0 -38
- package/dist/link-auth/expand-macro.d.ts.map +0 -1
- package/dist/link-auth/expand-macro.js +0 -133
- package/dist/link-auth/expand-macro.js.map +0 -1
- package/dist/link-auth/macros.yaml +0 -50
- package/dist/link-auth/resolve-token.d.ts +0 -83
- package/dist/link-auth/resolve-token.d.ts.map +0 -1
- package/dist/link-auth/resolve-token.js +0 -101
- package/dist/link-auth/resolve-token.js.map +0 -1
- package/dist/link-auth/resolve.d.ts +0 -102
- package/dist/link-auth/resolve.d.ts.map +0 -1
- package/dist/link-auth/resolve.js +0 -66
- package/dist/link-auth/resolve.js.map +0 -1
- package/dist/link-auth/rewrite.d.ts +0 -52
- package/dist/link-auth/rewrite.d.ts.map +0 -1
- package/dist/link-auth/rewrite.js +0 -102
- package/dist/link-auth/rewrite.js.map +0 -1
- package/dist/link-auth/select-provider.d.ts +0 -30
- package/dist/link-auth/select-provider.d.ts.map +0 -1
- package/dist/link-auth/select-provider.js +0 -55
- package/dist/link-auth/select-provider.js.map +0 -1
- package/dist/link-auth/template.d.ts +0 -40
- package/dist/link-auth/template.d.ts.map +0 -1
- package/dist/link-auth/template.js +0 -89
- package/dist/link-auth/template.js.map +0 -1
- package/dist/link-auth/transforms.d.ts +0 -46
- package/dist/link-auth/transforms.d.ts.map +0 -1
- package/dist/link-auth/transforms.js +0 -52
- package/dist/link-auth/transforms.js.map +0 -1
- package/dist/template-entry.d.ts +0 -10
- package/dist/template-entry.d.ts.map +0 -1
- package/dist/template-entry.js +0 -10
- package/dist/template-entry.js.map +0 -1
- package/dist/template.d.ts +0 -7
- package/dist/template.d.ts.map +0 -1
- package/dist/template.js +0 -18
- package/dist/template.js.map +0 -1
- package/eslint/rules/no-path-join.cjs +0 -14
- package/eslint/rules/no-path-relative.cjs +0 -14
- package/eslint/rules/no-path-resolve.cjs +0 -14
- package/eslint/rules/path-function-rule-factory.cjs +0 -496
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ESLint rule: no-self-package-import
|
|
3
|
+
*
|
|
4
|
+
* Disallow a file importing the package it already lives in **by that package's
|
|
5
|
+
* own name**. Inside `packages/foo/src`, write `./types.js`, never
|
|
6
|
+
* `@scope/foo`.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* // ❌ BAD — packages/agent-runtime/src/session/file-session-store.ts
|
|
10
|
+
* import type { SessionStore } from '@vibe-agent-toolkit/agent-runtime';
|
|
11
|
+
*
|
|
12
|
+
* // ✅ GOOD
|
|
13
|
+
* import type { SessionStore } from './types.js';
|
|
14
|
+
*
|
|
15
|
+
* ## Why this is a build-breaker and not a style preference
|
|
16
|
+
*
|
|
17
|
+
* A self-import resolves out through `node_modules` to the package's OWN
|
|
18
|
+
* `package.json`, whose `types`/`exports` point at `./dist/index.d.ts` — a file
|
|
19
|
+
* the compiler is in the middle of producing. It appears to work only because of
|
|
20
|
+
* a TypeScript courtesy: while `dist` IS the running project's output path,
|
|
21
|
+
* `dist/index.d.ts` is recognised as this project's own declaration output and
|
|
22
|
+
* the import is redirected back to `src`, so it resolves with no `dist/` on disk
|
|
23
|
+
* at all.
|
|
24
|
+
*
|
|
25
|
+
* That courtesy is conditional, and every condition is one a build script may
|
|
26
|
+
* legitimately change. Compile the package with `--outDir` pointed anywhere else
|
|
27
|
+
* — a staging directory used to make emit atomic, for instance — and `dist` is
|
|
28
|
+
* no longer this program's output, the redirect is gone, tsc looks for a literal
|
|
29
|
+
* `dist/index.d.ts`, and a tree that has never been built has none:
|
|
30
|
+
*
|
|
31
|
+
* error TS2307: Cannot find module '@scope/foo' or its corresponding type
|
|
32
|
+
* declarations.
|
|
33
|
+
*
|
|
34
|
+
* Knock-on `TS2339`s follow wherever a local type extended one of the now
|
|
35
|
+
* unresolved imports, which is what makes the failure read as a type bug in
|
|
36
|
+
* code nobody touched.
|
|
37
|
+
*
|
|
38
|
+
* ## Why the compiler cannot be trusted to find these
|
|
39
|
+
*
|
|
40
|
+
* The failure is invisible on any machine that has built the package before:
|
|
41
|
+
* a stale `dist/` satisfies the literal lookup, so the build passes — by
|
|
42
|
+
* typechecking the package against its PREVIOUS build's declarations. Worse, in
|
|
43
|
+
* a monorepo whose worktrees live inside the main checkout, module resolution
|
|
44
|
+
* walks up past the worktree into the parent checkout's `node_modules` and
|
|
45
|
+
* resolves against a DIFFERENT checkout's `dist/`. Both are green locally and
|
|
46
|
+
* red in CI, which is the only place the tree is genuinely pristine.
|
|
47
|
+
*
|
|
48
|
+
* So a self-import is latent by construction: it costs nothing until the day a
|
|
49
|
+
* build script changes `outDir`, and then it fails somewhere nobody can
|
|
50
|
+
* reproduce. Lint is the only stage that sees it on the author's machine.
|
|
51
|
+
*
|
|
52
|
+
* ## `packageName` is required, and the rule reads no files to get it
|
|
53
|
+
*
|
|
54
|
+
* The obvious implementation walks up from the linted file to the nearest
|
|
55
|
+
* `package.json` and reads its `name`. This rule deliberately does NOT: every
|
|
56
|
+
* module on the `./eslint` subpath is plain data that requires nothing — not
|
|
57
|
+
* `eslint`, not a third-party package, not even a Node builtin — which is what
|
|
58
|
+
* lets the pack ship as a subpath of a runtime package, keeps `eslint` an
|
|
59
|
+
* OPTIONAL peer dependency, and keeps the other subpaths resolving in a tree with
|
|
60
|
+
* no ESLint installed. `test/eslint/subpath-purity.test.ts` enforces that as an
|
|
61
|
+
* empty-set assertion. One `require('node:fs')` here would be the first crack in
|
|
62
|
+
* it, for a convenience the config layer can supply for free.
|
|
63
|
+
*
|
|
64
|
+
* So the caller names the package, and the caller is a config file that already
|
|
65
|
+
* runs in full Node and can read every manifest it likes. See the generated
|
|
66
|
+
* per-package blocks in this repo's `eslint.config.js`.
|
|
67
|
+
*
|
|
68
|
+
* Scoping is the caller's job for the same reason: only files a package actually
|
|
69
|
+
* COMPILES can break the build, and test and example trees commonly import their
|
|
70
|
+
* own package by name ON PURPOSE, to exercise the public entry point exactly as a
|
|
71
|
+
* consumer would.
|
|
72
|
+
*/
|
|
73
|
+
|
|
74
|
+
'use strict';
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Whether `specifier` names `packageName` itself or one of its subpaths.
|
|
78
|
+
*
|
|
79
|
+
* The subpath check is anchored on `/` so `@scope/foo-bar` is not read as a
|
|
80
|
+
* subpath of `@scope/foo`.
|
|
81
|
+
*
|
|
82
|
+
* @param {string} specifier - The import specifier as written.
|
|
83
|
+
* @param {string} packageName - The enclosing package's name.
|
|
84
|
+
* @returns {boolean}
|
|
85
|
+
*/
|
|
86
|
+
function isSelfReference(specifier, packageName) {
|
|
87
|
+
return specifier === packageName || specifier.startsWith(`${packageName}/`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
module.exports = {
|
|
91
|
+
meta: {
|
|
92
|
+
type: 'problem',
|
|
93
|
+
docs: {
|
|
94
|
+
description:
|
|
95
|
+
"Disallow importing the enclosing package by its own name; use a relative path so the import does not depend on the package's built `dist/`.",
|
|
96
|
+
category: 'Build correctness',
|
|
97
|
+
bans: 'importing the enclosing package by its own name',
|
|
98
|
+
useInstead: 'a relative path to the defining module',
|
|
99
|
+
// Not in `recommended`, for two reasons either of which would be enough. It
|
|
100
|
+
// REQUIRES a `packageName` option — it will not read a `package.json` to find
|
|
101
|
+
// out, because a rule module must require nothing (see `index.cjs`) — and a
|
|
102
|
+
// rule with a required option cannot ride in a config that supplies none.
|
|
103
|
+
// And the directories it must not fire in are a property of the adopter's
|
|
104
|
+
// `tsconfig`: a self-import only breaks the build in files the package
|
|
105
|
+
// COMPILES, while test and example trees import their own package by name on
|
|
106
|
+
// purpose. The consuming config knows both; see the README.
|
|
107
|
+
recommended: false,
|
|
108
|
+
recommendedSeverity: 'error',
|
|
109
|
+
},
|
|
110
|
+
messages: {
|
|
111
|
+
useRelativeImport:
|
|
112
|
+
"Do not import '{{specifier}}' — this file is already inside '{{packageName}}'. That resolves through the package's own `dist/`, which only works while `dist` is the running compiler's output path; change `outDir` (or build a tree that has never been built) and it fails with TS2307. Import the defining module by relative path instead.",
|
|
113
|
+
},
|
|
114
|
+
schema: [
|
|
115
|
+
{
|
|
116
|
+
type: 'object',
|
|
117
|
+
properties: {
|
|
118
|
+
packageName: { type: 'string', minLength: 1 },
|
|
119
|
+
},
|
|
120
|
+
required: ['packageName'],
|
|
121
|
+
additionalProperties: false,
|
|
122
|
+
},
|
|
123
|
+
],
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
create(context) {
|
|
127
|
+
const { packageName } = context.options[0];
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* @param {{ type: string, value?: unknown } | null | undefined} source - A node in source position.
|
|
131
|
+
*/
|
|
132
|
+
const check = (source) => {
|
|
133
|
+
if (!source || source.type !== 'Literal') return;
|
|
134
|
+
const specifier = source.value;
|
|
135
|
+
if (typeof specifier !== 'string') return;
|
|
136
|
+
if (!isSelfReference(specifier, packageName)) return;
|
|
137
|
+
context.report({
|
|
138
|
+
node: source,
|
|
139
|
+
messageId: 'useRelativeImport',
|
|
140
|
+
data: { specifier, packageName },
|
|
141
|
+
});
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
return {
|
|
145
|
+
// `import … from 'x'` and `import type … from 'x'`
|
|
146
|
+
ImportDeclaration: (node) => check(node.source),
|
|
147
|
+
// `export … from 'x'` and `export * from 'x'` — a barrel re-exporting
|
|
148
|
+
// through its own package name is the same resolution, one step removed.
|
|
149
|
+
ExportNamedDeclaration: (node) => check(node.source),
|
|
150
|
+
ExportAllDeclaration: (node) => check(node.source),
|
|
151
|
+
// `await import('x')`
|
|
152
|
+
ImportExpression: (node) => check(node.source),
|
|
153
|
+
// `import('x')` in TYPE position, which no other visitor above reaches.
|
|
154
|
+
// The specifier hangs off `source`; typescript-eslint has called this
|
|
155
|
+
// property `parameter` and `argument` in earlier majors, and reading the
|
|
156
|
+
// wrong one costs nothing at lint time — the visitor simply never fires.
|
|
157
|
+
// The `import() in TYPE position` case in `test/eslint/rules/no-self-package-import.test.ts` is what turns
|
|
158
|
+
// that silence into a red test if a future parser renames it again.
|
|
159
|
+
TSImportType: (node) => check(node.source),
|
|
160
|
+
// `require('x')` in the `.cts`/`.cjs` files this pack also lints.
|
|
161
|
+
CallExpression: (node) => {
|
|
162
|
+
if (node.callee.type !== 'Identifier' || node.callee.name !== 'require') return;
|
|
163
|
+
if (node.arguments.length !== 1) return;
|
|
164
|
+
check(node.arguments[0]);
|
|
165
|
+
},
|
|
166
|
+
};
|
|
167
|
+
},
|
|
168
|
+
};
|
|
@@ -68,9 +68,16 @@ module.exports = {
|
|
|
68
68
|
meta: {
|
|
69
69
|
type: 'suggestion',
|
|
70
70
|
docs: {
|
|
71
|
-
description:
|
|
72
|
-
|
|
73
|
-
|
|
71
|
+
description:
|
|
72
|
+
'Disallow function declarations inside test blocks (SonarQube S1515)',
|
|
73
|
+
category: 'Code and test hygiene',
|
|
74
|
+
bans: 'helper functions declared inside `describe`/`it`',
|
|
75
|
+
useInstead: 'module scope',
|
|
76
|
+
// Not in `recommended`: a position on TEST STYLE, not a portability or
|
|
77
|
+
// correctness fact. Someone installing this package for `safePath.join()`
|
|
78
|
+
// should not silently inherit a test convention. Enable it by name.
|
|
79
|
+
recommended: false,
|
|
80
|
+
recommendedSeverity: 'error',
|
|
74
81
|
},
|
|
75
82
|
messages: {
|
|
76
83
|
moveToModuleScope:
|
|
@@ -85,9 +85,13 @@ module.exports = {
|
|
|
85
85
|
meta: {
|
|
86
86
|
type: 'problem',
|
|
87
87
|
docs: {
|
|
88
|
-
description:
|
|
89
|
-
|
|
88
|
+
description:
|
|
89
|
+
'Prevent Unix-specific commands that break Windows compatibility',
|
|
90
|
+
category: 'Filesystem and process',
|
|
91
|
+
bans: '`tar`, `grep`, `rm`, `echo`, … spawned directly',
|
|
92
|
+
useInstead: 'Node APIs, or a portable script fixture',
|
|
90
93
|
recommended: true,
|
|
94
|
+
recommendedSeverity: 'error',
|
|
91
95
|
},
|
|
92
96
|
fixable: null, // No auto-fix - requires manual refactoring
|
|
93
97
|
schema: [],
|
|
@@ -41,8 +41,29 @@ module.exports = {
|
|
|
41
41
|
docs: {
|
|
42
42
|
description:
|
|
43
43
|
'Enforce safePath.joinUnderRoot() for joins whose first arg is a security root variable (name ends in "Root").',
|
|
44
|
-
category: '
|
|
45
|
-
|
|
44
|
+
category: 'Path handling',
|
|
45
|
+
bans: '`safePath.join(someRoot, x)` where `x` can escape',
|
|
46
|
+
useInstead: '`safePath.joinUnderRoot()`',
|
|
47
|
+
subpath: '/path',
|
|
48
|
+
// Not in `recommended`: an unsound heuristic, pending a rewrite. It keys on
|
|
49
|
+
// whether an identifier's NAME ends in `root` rather than on whether any
|
|
50
|
+
// segment is caller-controlled, so it is noisy and blind at once — measured
|
|
51
|
+
// on a 4,670-file adopter tree: 108 findings, 0 autofixable, and every one
|
|
52
|
+
// of these verified by execution:
|
|
53
|
+
//
|
|
54
|
+
// FIRES safePath.join(repoRoot, 'docs', 'product') <- all literals, cannot escape
|
|
55
|
+
// FIRES safePath.resolve(packageRoot, '..', '..') <- escaping IS the intent
|
|
56
|
+
// FIRES safePath.join(repoRoot) <- one argument, no segment
|
|
57
|
+
// silent safePath.join(base, userInput) <- THE dangerous shape, missed
|
|
58
|
+
//
|
|
59
|
+
// A rule that misses the case it exists to catch must not ride in a config
|
|
60
|
+
// named `recommended` at any severity — a safety core that cries wolf
|
|
61
|
+
// teaches people to ignore it, which costs the true positives too. It still
|
|
62
|
+
// earns `error` where scoped to directories in which a path escape is a
|
|
63
|
+
// security boundary (VAT scopes it to the skill-test staging code).
|
|
64
|
+
// Re-include it when it keys on taint rather than on naming.
|
|
65
|
+
recommended: false,
|
|
66
|
+
recommendedSeverity: 'error',
|
|
46
67
|
},
|
|
47
68
|
messages: {
|
|
48
69
|
useJoinUnderRoot:
|
|
@@ -77,8 +77,12 @@ module.exports = {
|
|
|
77
77
|
docs: {
|
|
78
78
|
description:
|
|
79
79
|
'Disallow `.pathname` on a URL built from `import.meta.url`; use `fileURLToPath()` instead.',
|
|
80
|
-
category: '
|
|
80
|
+
category: 'URLs and dynamic imports',
|
|
81
|
+
bans: '`new URL(x, import.meta.url).pathname` as a filesystem path',
|
|
82
|
+
useInstead: '`resolveFromImportMeta()` / `fileURLToPath()`',
|
|
83
|
+
subpath: '/fs',
|
|
81
84
|
recommended: true,
|
|
85
|
+
recommendedSeverity: 'error',
|
|
82
86
|
},
|
|
83
87
|
messages: {
|
|
84
88
|
useFileURLToPath:
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ESLint rule: no-version-literal
|
|
3
|
+
*
|
|
4
|
+
* The mechanical half of CLAUDE.md's "NO VERSIONS" rule. Flags the two shapes
|
|
5
|
+
* a hand-maintained version integer takes:
|
|
6
|
+
*
|
|
7
|
+
* 1. `z.literal(<number>)` on a schema field named like a version
|
|
8
|
+
* (`version`, `schemaVersion`, `formatVersion`, `*_VERSION`, …), with or
|
|
9
|
+
* without a trailing `.optional()` / `.describe()`;
|
|
10
|
+
* 2. a variable, `let`, or class field named `<X>_VERSION`, `<X>_REVISION`,
|
|
11
|
+
* `VERSION`, `fooVersion`, … initialised to a numeric literal.
|
|
12
|
+
*
|
|
13
|
+
* Both are a number a human must remember to bump for stored data to be
|
|
14
|
+
* judged valid, and nothing fails when they forget: the number and the shape
|
|
15
|
+
* it claims to describe drift apart silently, and the failure surfaces later
|
|
16
|
+
* as a reader confidently mis-parsing data it should have refused. A strict
|
|
17
|
+
* schema already answers "can I read this?" and moves when the shape moves,
|
|
18
|
+
* for whoever made the edit; a derived digest answers "did these come from
|
|
19
|
+
* the same shape?". The integer answers neither and costs a permanent human
|
|
20
|
+
* obligation.
|
|
21
|
+
*
|
|
22
|
+
* ## Not offenders, and why the rule already leaves them alone
|
|
23
|
+
*
|
|
24
|
+
* - `ANTHROPIC_VERSION = '2023-06-01'` — a STRING header value, an external
|
|
25
|
+
* fact; the rule only fires on a NUMERIC initialiser.
|
|
26
|
+
* - `SUPPORTED_PYTHON_VERSIONS = [...]` — a real list; the name ends in
|
|
27
|
+
* `VERSIONS`, not `VERSION`, and the value is an array.
|
|
28
|
+
* - a regex that PARSES versions; a `const VERSION = '…'` in a test fixture.
|
|
29
|
+
* - `version: z.string()` / `z.number()` — a field that CARRIES a version is
|
|
30
|
+
* fine; a field pinned to one integer is the offender.
|
|
31
|
+
*
|
|
32
|
+
* Option `allowNames: string[]` — identifier / property names exempt from the
|
|
33
|
+
* rule. Ship it empty: the documented non-offenders need no entry, so any
|
|
34
|
+
* entry here is a version constant somebody decided to keep, and CLAUDE.md
|
|
35
|
+
* says there is no such case.
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* // BAD — bumped from memory, or not
|
|
39
|
+
* const CACHE_VERSION = 3;
|
|
40
|
+
* const Cached = z.object({ version: z.literal(3), … });
|
|
41
|
+
*
|
|
42
|
+
* // GOOD — the strict schema IS the validity check
|
|
43
|
+
* const Cached = z.object({ … }).strict();
|
|
44
|
+
*/
|
|
45
|
+
|
|
46
|
+
'use strict';
|
|
47
|
+
|
|
48
|
+
/** A name that reads as a version or revision: `X_VERSION`, `VERSION`, `fooVersion`, `version`. */
|
|
49
|
+
const VERSION_NAME = /(?:^|_)(?:VERSION|REVISION)$|(?:^|[a-z])(?:Version|Revision)$|^(?:version|revision)$/u;
|
|
50
|
+
|
|
51
|
+
/** The numeric literal an initialiser reduces to, seeing through `as const`, or null. */
|
|
52
|
+
function numericInitialiser(init) {
|
|
53
|
+
if (!init) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
if (init.type === 'TSAsExpression' || init.type === 'TSSatisfiesExpression') {
|
|
57
|
+
return numericInitialiser(init.expression);
|
|
58
|
+
}
|
|
59
|
+
return init.type === 'Literal' && typeof init.value === 'number' ? init : null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** The plain name of a declarator id, property key, or class field key, or null. */
|
|
63
|
+
function keyName(key) {
|
|
64
|
+
if (key.type === 'Identifier') {
|
|
65
|
+
return key.name;
|
|
66
|
+
}
|
|
67
|
+
return key.type === 'Literal' && typeof key.value === 'string' ? key.value : null;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Whether a property value's chain roots at `z.literal(<number>)`. */
|
|
71
|
+
function rootsAtNumericZodLiteral(value) {
|
|
72
|
+
let current = value;
|
|
73
|
+
while (current.type === 'CallExpression' && current.callee.type === 'MemberExpression') {
|
|
74
|
+
const { callee } = current;
|
|
75
|
+
const isZodLiteral =
|
|
76
|
+
callee.object.type === 'Identifier' &&
|
|
77
|
+
callee.object.name === 'z' &&
|
|
78
|
+
callee.property.type === 'Identifier' &&
|
|
79
|
+
callee.property.name === 'literal';
|
|
80
|
+
if (isZodLiteral) {
|
|
81
|
+
return numericInitialiser(current.arguments[0]) !== null;
|
|
82
|
+
}
|
|
83
|
+
current = callee.object;
|
|
84
|
+
}
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
module.exports = {
|
|
89
|
+
meta: {
|
|
90
|
+
type: 'problem',
|
|
91
|
+
docs: {
|
|
92
|
+
description:
|
|
93
|
+
'Disallow z.literal(<number>) on a version-named field and <X>_VERSION = <number> ' +
|
|
94
|
+
'constants — a hand-bumped integer deciding data validity is the shape CLAUDE.md bans',
|
|
95
|
+
recommended: false,
|
|
96
|
+
recommendedSeverity: 'warn',
|
|
97
|
+
},
|
|
98
|
+
schema: [
|
|
99
|
+
{
|
|
100
|
+
type: 'object',
|
|
101
|
+
properties: {
|
|
102
|
+
allowNames: { type: 'array', items: { type: 'string' }, uniqueItems: true },
|
|
103
|
+
},
|
|
104
|
+
additionalProperties: false,
|
|
105
|
+
},
|
|
106
|
+
],
|
|
107
|
+
messages: {
|
|
108
|
+
versionConstant:
|
|
109
|
+
'{{name}} is a hand-maintained version integer: nothing fails when it is not bumped, so ' +
|
|
110
|
+
'the number and the shape drift apart silently. Delete it — a .strict() schema already ' +
|
|
111
|
+
'refuses data it cannot read, and a digest of the shape answers "same shape?" without ' +
|
|
112
|
+
'anyone remembering a number.',
|
|
113
|
+
versionLiteralField:
|
|
114
|
+
'{{name}}: z.literal(<number>) pins stored data to an integer a human must remember to ' +
|
|
115
|
+
'bump. Delete the field and let the .strict() schema decide validity, or derive a digest ' +
|
|
116
|
+
'of the shape instead.',
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
|
|
120
|
+
create(context) {
|
|
121
|
+
const allow = new Set(context.options?.[0]?.allowNames ?? []);
|
|
122
|
+
|
|
123
|
+
function checkNamedInitialiser(node, key, init) {
|
|
124
|
+
const name = keyName(key);
|
|
125
|
+
if (name === null || allow.has(name) || !VERSION_NAME.test(name)) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
if (numericInitialiser(init) !== null) {
|
|
129
|
+
context.report({ node, messageId: 'versionConstant', data: { name } });
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return {
|
|
134
|
+
VariableDeclarator(node) {
|
|
135
|
+
checkNamedInitialiser(node, node.id, node.init);
|
|
136
|
+
},
|
|
137
|
+
PropertyDefinition(node) {
|
|
138
|
+
checkNamedInitialiser(node, node.key, node.value);
|
|
139
|
+
},
|
|
140
|
+
Property(node) {
|
|
141
|
+
const name = keyName(node.key);
|
|
142
|
+
if (name === null || allow.has(name) || !VERSION_NAME.test(name)) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
if (rootsAtNumericZodLiteral(node.value)) {
|
|
146
|
+
context.report({ node, messageId: 'versionLiteralField', data: { name } });
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
},
|
|
151
|
+
};
|
|
@@ -195,7 +195,16 @@ module.exports = {
|
|
|
195
195
|
docs: {
|
|
196
196
|
description:
|
|
197
197
|
String.raw`Prefer String#startsWith / String#endsWith over /^literal/.test() — including escaped literals such as \/ and \*, and regexes held in a const`,
|
|
198
|
+
category: 'Code and test hygiene',
|
|
199
|
+
bans: '`/^foo/.test(s)`, `` /^\\*glob/.test(s) ``, `const RE = /^foo/; RE.test(s)`',
|
|
200
|
+
useInstead: "`s.startsWith('foo')`",
|
|
201
|
+
// `error`, deliberately. This rule was briefly graded on a different axis
|
|
202
|
+
// ("style, not a defect") and demoted to `warn` — wrong twice over: avoiding
|
|
203
|
+
// a SonarQube S6557 at lint time instead of at merge time is a real saving,
|
|
204
|
+
// and the matcher rejects any regex containing a metacharacter, so it only
|
|
205
|
+
// fires on true literal prefixes and has near-zero churn.
|
|
198
206
|
recommended: true,
|
|
207
|
+
recommendedSeverity: 'error',
|
|
199
208
|
},
|
|
200
209
|
messages: {
|
|
201
210
|
// `{{pattern}}` carries its FLAGS. Rendering `/^abc/` for a source
|
|
@@ -253,8 +253,14 @@ module.exports = {
|
|
|
253
253
|
docs: {
|
|
254
254
|
description:
|
|
255
255
|
'Require a tracking-issue justification for skipped tests, and ban tautological assertions',
|
|
256
|
-
category: '
|
|
257
|
-
|
|
256
|
+
category: 'Code and test hygiene',
|
|
257
|
+
bans: 'unannotated `it.skip`/`it.todo`, tautological assertions, empty test bodies',
|
|
258
|
+
useInstead: 'a `SKIP(#123): reason` annotation, or a real assertion',
|
|
259
|
+
// Not in `recommended`: a position on TEST STYLE, not a portability or
|
|
260
|
+
// correctness fact. Someone installing this package for `safePath.join()`
|
|
261
|
+
// should not silently inherit a test convention. Enable it by name.
|
|
262
|
+
recommended: false,
|
|
263
|
+
recommendedSeverity: 'error',
|
|
258
264
|
},
|
|
259
265
|
schema: [],
|
|
260
266
|
messages: {
|
|
@@ -106,7 +106,7 @@ function insertAboveWithComments(fixer, sourceCode, node, text) {
|
|
|
106
106
|
* PER-RULE rather than one shared `settings` key, because a seam does not have
|
|
107
107
|
* to split its symbols the way this package does. The same adopter's narrow
|
|
108
108
|
* entry carries `normalizedTmpdir`/`mkdirSyncReal` but NOT `safePath`, so their
|
|
109
|
-
* `no-os-tmpdir` and `no-path
|
|
109
|
+
* `no-os-tmpdir` and `no-raw-node-path` need different targets — the exact "path
|
|
110
110
|
* rules here, fs rules there" case a single key cannot express.
|
|
111
111
|
*/
|
|
112
112
|
const SAFE_MODULE_PROPERTY = Object.freeze({
|
package/package.json
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vibe-agent-toolkit/utils",
|
|
3
|
-
"version": "0.2.0-rc.
|
|
3
|
+
"version": "0.2.0-rc.10",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Core utility functions shared across the vibe-agent-toolkit packages",
|
|
6
|
-
"sideEffects":
|
|
6
|
+
"sideEffects": [
|
|
7
|
+
"./dist/crawl-timing.js"
|
|
8
|
+
],
|
|
7
9
|
"keywords": [
|
|
8
10
|
"typescript",
|
|
9
11
|
"utilities",
|
|
@@ -15,7 +17,7 @@
|
|
|
15
17
|
"author": "Jeff Dutton",
|
|
16
18
|
"license": "MIT",
|
|
17
19
|
"engines": {
|
|
18
|
-
"node": ">=22.
|
|
20
|
+
"node": ">=22.13.0"
|
|
19
21
|
},
|
|
20
22
|
"main": "./dist/index.js",
|
|
21
23
|
"types": "./dist/index.d.ts",
|
|
@@ -32,6 +34,10 @@
|
|
|
32
34
|
"types": "./dist/fs.d.ts",
|
|
33
35
|
"import": "./dist/fs.js"
|
|
34
36
|
},
|
|
37
|
+
"./text": {
|
|
38
|
+
"types": "./dist/text.d.ts",
|
|
39
|
+
"import": "./dist/text.js"
|
|
40
|
+
},
|
|
35
41
|
"./process": {
|
|
36
42
|
"types": "./dist/process.d.ts",
|
|
37
43
|
"import": "./dist/process.js"
|
|
@@ -48,9 +54,9 @@
|
|
|
48
54
|
"types": "./dist/zod.d.ts",
|
|
49
55
|
"import": "./dist/zod.js"
|
|
50
56
|
},
|
|
51
|
-
"./
|
|
52
|
-
"types": "./dist/
|
|
53
|
-
"import": "./dist/
|
|
57
|
+
"./skill-test": {
|
|
58
|
+
"types": "./dist/skill-test/index.d.ts",
|
|
59
|
+
"import": "./dist/skill-test/index.js"
|
|
54
60
|
},
|
|
55
61
|
"./yaml": {
|
|
56
62
|
"types": "./dist/yaml.d.ts",
|
|
@@ -84,20 +90,19 @@
|
|
|
84
90
|
"README.md"
|
|
85
91
|
],
|
|
86
92
|
"scripts": {
|
|
87
|
-
"build": "
|
|
93
|
+
"build": "tsx ../dev-tools/src/tsc-clean-build.ts",
|
|
88
94
|
"test:unit": "vitest run",
|
|
89
95
|
"test:integration": "vitest run --config vitest.integration.config.ts",
|
|
90
|
-
"test:system": "vitest run --config vitest.system.config.ts",
|
|
91
96
|
"test:watch": "vitest",
|
|
92
97
|
"typecheck": "tsc --noEmit",
|
|
93
|
-
"clean": "rimraf dist *.tsbuildinfo"
|
|
98
|
+
"clean": "rimraf --glob dist \"*.tsbuildinfo\" .tsc-staging"
|
|
94
99
|
},
|
|
95
100
|
"dependencies": {
|
|
96
|
-
"
|
|
101
|
+
"@vibe-validate/git": "0.20.1",
|
|
97
102
|
"ignore": "^7.0.5",
|
|
98
103
|
"picomatch": "^4.0.3",
|
|
99
104
|
"which": "^5.0.0",
|
|
100
|
-
"yaml": "^2.
|
|
105
|
+
"yaml": "^2.9.0"
|
|
101
106
|
},
|
|
102
107
|
"peerDependencies": {
|
|
103
108
|
"eslint": ">=9.0.0"
|
|
@@ -108,12 +113,7 @@
|
|
|
108
113
|
}
|
|
109
114
|
},
|
|
110
115
|
"devDependencies": {
|
|
111
|
-
"@types/picomatch": "^4.0.2",
|
|
112
116
|
"@types/which": "^3.0.4",
|
|
113
|
-
"eslint": "^9.39.5",
|
|
114
|
-
"rimraf": "^6.0.1",
|
|
115
|
-
"typescript": "^5.9.3",
|
|
116
|
-
"vitest": "^3.2.4",
|
|
117
117
|
"zod": "^3.25.0"
|
|
118
118
|
},
|
|
119
119
|
"publishConfig": {
|
|
@@ -1,34 +0,0 @@
|
|
|
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
|
|
@@ -1 +0,0 @@
|
|
|
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"}
|
|
@@ -1,58 +0,0 @@
|
|
|
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
|
|
@@ -1 +0,0 @@
|
|
|
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"}
|