@mirasen/license-gate 1.0.0

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,151 @@
1
+ /**
2
+ * Internal types for @mirasen/license-gate.
3
+ *
4
+ * Closed discriminated unions throughout. Reporters and command layers should
5
+ * exhaustively switch on `outcome` and `kind` so that adding a new variant is
6
+ * a compile-time signal across the codebase.
7
+ */
8
+ /** Sentinel string used when a package's license cannot be determined from
9
+ * `package.json#license` (missing, empty, or non-string shape). */
10
+ export declare const LICENSE_COULD_NOT_DETERMINE = "could not determine";
11
+ export type LicenseCouldNotDetermine = typeof LICENSE_COULD_NOT_DETERMINE;
12
+ /** A flat record of one physically-installed package copy. `path` is the
13
+ * copy's location relative to the selected project root; two copies of the
14
+ * same `name@version` at different physical paths are still distinct
15
+ * records. */
16
+ export type InstalledPackageRecord = {
17
+ name: string;
18
+ version: string;
19
+ /** `${name}@${version}` — convenience composite. */
20
+ packageId: string;
21
+ /** Path of the installed copy relative to the selected project root,
22
+ * using POSIX-style `/` separators. The project root itself is exposed
23
+ * as `"."`. Internal graph/fs logic continues to use absolute paths;
24
+ * this field is the public, redaction-safe form. Physical placement of
25
+ * an installed copy (hoisted vs. workspace-local) is communicated
26
+ * entirely through this field. */
27
+ path: string;
28
+ /** Trimmed verbatim string from `package.json#license`, or the
29
+ * `LICENSE_COULD_NOT_DETERMINE` sentinel. Never normalised. */
30
+ license: string | LicenseCouldNotDetermine;
31
+ repository?: string;
32
+ publisher?: string;
33
+ email?: string;
34
+ };
35
+ /** A parsed `licenses/allowed-hard.txt`. Keys are the literal accepted license
36
+ * strings (after trim, dedup, and skipping blank/comment lines). */
37
+ export type AllowedHardList = ReadonlySet<string>;
38
+ /** A parsed rule from `licenses/allowed-packages.txt`. The original line text
39
+ * is preserved verbatim for inclusion in reports as `matchedPackageRule`. */
40
+ export type AllowedPackagesRule = {
41
+ kind: 'scope';
42
+ scope: string;
43
+ ruleText: string;
44
+ } | {
45
+ kind: 'package-version';
46
+ name: string;
47
+ version: string;
48
+ ruleText: string;
49
+ } | {
50
+ kind: 'scoped-package-version';
51
+ scope: string;
52
+ name: string;
53
+ version: string;
54
+ ruleText: string;
55
+ } | {
56
+ kind: 'package-name';
57
+ name: string;
58
+ ruleText: string;
59
+ } | {
60
+ kind: 'scoped-package-name';
61
+ scope: string;
62
+ name: string;
63
+ ruleText: string;
64
+ };
65
+ /** Parsed allowlists used by the policy evaluator. */
66
+ export type AllowedPackages = ReadonlyArray<AllowedPackagesRule>;
67
+ /** A policy decision for one installed package record. */
68
+ export type Decision = {
69
+ record: InstalledPackageRecord;
70
+ outcome: 'allowed-by-license';
71
+ } | {
72
+ record: InstalledPackageRecord;
73
+ outcome: 'allowed-by-scope-rule';
74
+ matchedPackageRule: string;
75
+ } | {
76
+ record: InstalledPackageRecord;
77
+ outcome: 'allowed-by-package-version-rule';
78
+ matchedPackageRule: string;
79
+ } | {
80
+ record: InstalledPackageRecord;
81
+ outcome: 'allowed-by-package-name-rule';
82
+ matchedPackageRule: string;
83
+ } | {
84
+ record: InstalledPackageRecord;
85
+ outcome: 'violation';
86
+ reason: ViolationReason;
87
+ };
88
+ /** The two top-level policy violation reasons. SPDX outcomes are diagnostic
89
+ * detail under `license-not-in-allowlist`. */
90
+ export type ViolationReason = {
91
+ kind: 'license-not-in-allowlist';
92
+ /** The original license string from package.json, verbatim. */
93
+ raw: string;
94
+ /** Diagnostic detail explaining how the SPDX evaluator landed here. */
95
+ detailCode?: 'literal-not-allowed-and-spdx-unparseable' | 'spdx-expression-not-satisfied';
96
+ /** Present iff `detailCode === "spdx-expression-not-satisfied"`. */
97
+ offendingLeaves?: string[];
98
+ } | {
99
+ kind: 'package-not-in-allowlist';
100
+ };
101
+ /** Configuration / runtime errors that halt execution before policy
102
+ * evaluation completes. They map to exit code 2. */
103
+ export type ConfigError = {
104
+ kind: 'missing-package-json';
105
+ cwd: string;
106
+ } | {
107
+ kind: 'missing-node-modules';
108
+ cwd: string;
109
+ } | {
110
+ kind: 'missing-allowed-hard-file';
111
+ path: string;
112
+ } | {
113
+ kind: 'invalid-package-override-rule';
114
+ path: string;
115
+ lineNumber: number;
116
+ line: string;
117
+ } | {
118
+ kind: 'invalid-workspace';
119
+ query: string;
120
+ reason: string;
121
+ } | {
122
+ kind: 'output-path-unwritable';
123
+ path: string;
124
+ cause: string;
125
+ } | {
126
+ kind: 'invalid-usage';
127
+ message: string;
128
+ };
129
+ /** Custom error wrapper so the CLI can recognise and exit 2 cleanly. */
130
+ export declare class LicenseGateConfigError extends Error {
131
+ readonly detail: ConfigError;
132
+ constructor(detail: ConfigError, message: string);
133
+ }
134
+ /** A `collect` row — the same as `InstalledPackageRecord` plus a flag for the
135
+ * project-root entry, which is included in `collect` output but skipped from
136
+ * `check` evaluation. */
137
+ export type CollectedRecord = InstalledPackageRecord & {
138
+ isProjectRoot?: boolean;
139
+ };
140
+ /** The result returned by the programmatic `check()` API. */
141
+ export type CheckResult = {
142
+ decisions: Decision[];
143
+ violations: Array<Extract<Decision, {
144
+ outcome: 'violation';
145
+ }>>;
146
+ skippedProjectRoot: InstalledPackageRecord | null;
147
+ };
148
+ /** The result returned by the programmatic `collect()` API. */
149
+ export type CollectResult = {
150
+ records: CollectedRecord[];
151
+ };
package/dist/types.js ADDED
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Internal types for @mirasen/license-gate.
3
+ *
4
+ * Closed discriminated unions throughout. Reporters and command layers should
5
+ * exhaustively switch on `outcome` and `kind` so that adding a new variant is
6
+ * a compile-time signal across the codebase.
7
+ */
8
+ /** Sentinel string used when a package's license cannot be determined from
9
+ * `package.json#license` (missing, empty, or non-string shape). */
10
+ export const LICENSE_COULD_NOT_DETERMINE = 'could not determine';
11
+ /** Custom error wrapper so the CLI can recognise and exit 2 cleanly. */
12
+ export class LicenseGateConfigError extends Error {
13
+ detail;
14
+ constructor(detail, message) {
15
+ super(message);
16
+ this.name = 'LicenseGateConfigError';
17
+ this.detail = detail;
18
+ }
19
+ }
package/package.json ADDED
@@ -0,0 +1,89 @@
1
+ {
2
+ "name": "@mirasen/license-gate",
3
+ "description": "Strict local license policy gate for npm projects.",
4
+ "version": "1.0.0",
5
+ "license": "MIT",
6
+ "author": {
7
+ "name": "Mirasen"
8
+ },
9
+ "keywords": [
10
+ "mirasen",
11
+ "license",
12
+ "license-gate",
13
+ "license-check",
14
+ "license-checker",
15
+ "license-policy",
16
+ "license-compliance",
17
+ "dependency-licenses",
18
+ "npm",
19
+ "workspaces",
20
+ "monorepo",
21
+ "spdx",
22
+ "ci",
23
+ "typescript"
24
+ ],
25
+ "homepage": "https://mirasen.io/license-gate",
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "git+https://github.com/mirasen-io/license-gate.git"
29
+ },
30
+ "bugs": {
31
+ "url": "https://github.com/mirasen-io/license-gate/issues"
32
+ },
33
+ "type": "module",
34
+ "types": "./dist/index.d.ts",
35
+ "bin": {
36
+ "license-gate": "./dist/cli.js"
37
+ },
38
+ "exports": {
39
+ ".": {
40
+ "types": "./dist/index.d.ts",
41
+ "import": "./dist/index.js",
42
+ "default": "./dist/index.js"
43
+ }
44
+ },
45
+ "scripts": {
46
+ "build": "rimraf ./dist && tsc --project tsconfig.json && npm run postbuild && npm run prepack",
47
+ "build:release": "rimraf ./dist && tsc --project tsconfig-release.json && npm run postbuild && npm run prepack",
48
+ "postbuild": "node scripts/postbuild.js",
49
+ "prepack": "publint",
50
+ "format": "prettier --write .",
51
+ "lint": "prettier --check . && eslint .",
52
+ "check": "tsc --project tsconfig.json --noEmit",
53
+ "check:test": "tsc --project tsconfig-test.json",
54
+ "test": "npm run check:test && vitest run --run",
55
+ "coverage": "npm run check:test && vitest run --coverage",
56
+ "changeset:version": "changeset version && npm install && git add --all",
57
+ "changeset:publish": "changeset publish"
58
+ },
59
+ "files": [
60
+ "dist",
61
+ "CHANGELOG.md",
62
+ "!dist/**/*.test.*",
63
+ "!dist/**/*.spec.*"
64
+ ],
65
+ "dependencies": {
66
+ "@npmcli/arborist": "^9.7.0",
67
+ "spdx-expression-parse": "^4.0.0"
68
+ },
69
+ "devDependencies": {
70
+ "@changesets/cli": "^2.28.1",
71
+ "@eslint/compat": "^2.0.0",
72
+ "@eslint/js": "^10.0.1",
73
+ "@types/node": "^25.0.2",
74
+ "@types/npmcli__arborist": "^6.3.3",
75
+ "@vitest/coverage-v8": "^4.1.8",
76
+ "eslint": "^10.0.2",
77
+ "eslint-config-prettier": "^10.1.2",
78
+ "globals": "^17.4.0",
79
+ "prettier": "^3.5.3",
80
+ "publint": "^0.3.11",
81
+ "rimraf": "^6.0.1",
82
+ "typescript": "^6.0.3",
83
+ "typescript-eslint": "^8.57.2",
84
+ "vitest": "^4.0.3"
85
+ },
86
+ "engines": {
87
+ "node": ">=22.9.0"
88
+ }
89
+ }