@promptci/core 0.2.1 → 0.2.2

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,127 @@
1
+ /**
2
+ * A containment-safe view of the scanned repository, for detectors that check
3
+ * whether a path an instruction file names exists (pcic-xhzb, pcic-x1vl).
4
+ *
5
+ * Instruction files are untrusted input. In the hosted product the scan root
6
+ * is a server temp directory, so an existence check on a path the text names
7
+ * (`../../etc/x`, `/etc/x`, `C:\x`, or a committed link out of the tree) would
8
+ * tell whoever wrote the scanned repository whether a server path exists.
9
+ * Every such check goes through {@link RepoTree}, which never touches a path
10
+ * outside the root.
11
+ */
12
+ import * as fs from 'node:fs';
13
+ import type fg from 'fast-glob';
14
+ import type { IssueCategory, PromptCiIssue } from './types.js';
15
+ /**
16
+ * True for a path that is absolute on ANY platform: POSIX (`/x`), Windows drive
17
+ * or UNC (`C:\x`, `\\host\share`), or drive-relative (`C:x`, which Windows
18
+ * resolves against that drive's working directory and Linux reads as a file
19
+ * name). Detectors skip these before any lookup, so a Windows and a Linux scan
20
+ * agree, the verdict never depends on where the repository is checked out, and
21
+ * a Windows scan never opens an SMB connection for `\\host\…`.
22
+ */
23
+ export declare function isAbsoluteAnywhere(p: string): boolean;
24
+ /** What the repository holds at a path. */
25
+ export type Lookup = {
26
+ state: 'found';
27
+ path: string;
28
+ isDir: boolean;
29
+ } | {
30
+ state: 'missing';
31
+ }
32
+ /** The path, or a link on the way to it, leads outside the scan root. */
33
+ | {
34
+ state: 'outside';
35
+ }
36
+ /** A directory on the way could not be listed: neither present nor absent. */
37
+ | {
38
+ state: 'unreadable';
39
+ };
40
+ /** The `code` of a filesystem error (`EACCES`, `ENOENT`, …), when it has one. */
41
+ export declare function errorCode(err: unknown): string | undefined;
42
+ /**
43
+ * pcic-xhzb: the only way a detector asks the filesystem about a path an
44
+ * instruction file names (dead references; command validity since pcic-x1vl).
45
+ *
46
+ * - Contained. A path is looked up by listing the directories from the scan
47
+ * root down to it, so nothing outside the root is ever listed, stat'ed or
48
+ * opened. A committed link is read (`readlink`, a property of the link,
49
+ * which lives in the tree) and followed only while its target stays inside
50
+ * the root; a link out of the tree makes the lookup `outside` without
51
+ * touching what it points at. A hosted scan runs against a server temp
52
+ * directory, and an existence check outside it would tell whoever wrote the
53
+ * scanned repository whether a server path exists.
54
+ * - Exact case. A segment matches a directory entry of exactly that name, so
55
+ * `Deploy_PR.yml` does not resolve to `deploy_pr.yml` on a case-insensitive
56
+ * filesystem (Windows, macOS). The result is the same on every platform, and
57
+ * the same as on Linux and GitHub, where the reference is broken.
58
+ * - Honest about failure. A directory that cannot be listed makes the lookup
59
+ * `unreadable`, never `missing`, and is recorded so the scan can say so.
60
+ *
61
+ * Listings are cached for the scan, so each directory is read at most once.
62
+ */
63
+ export declare class RepoTree {
64
+ readonly root: string;
65
+ /** The root's real path, when it differs (a root under a symlink, like macOS `/var`). */
66
+ private readonly realRoot;
67
+ private readonly listings;
68
+ /** What could not be read, as shown in the report (`dir/` or a link's path) → why. */
69
+ readonly unreadable: Map<string, string>;
70
+ constructor(repoRoot: string);
71
+ /** Forward-slash path relative to the root (`.` for the root itself); never absolute. */
72
+ relative(abs: string): string;
73
+ /** Record what could not be read: a directory (shown as `dir/`) or a link (shown as its path). */
74
+ noteUnreadable(p: string, reason: string, kind?: 'dir' | 'link'): void;
75
+ /** The entries of an in-root directory, by exact name. */
76
+ private list;
77
+ /** In-root equivalent of a link target written against the root's real path. */
78
+ private fromRealRoot;
79
+ lookup(candidate: string): Lookup;
80
+ isDir(candidate: string): boolean;
81
+ /**
82
+ * An extension-less module path names the file that carries the extension:
83
+ * `src/lib/db` is `src/lib/db.ts`. True when the directory holds `<name>.*`.
84
+ */
85
+ hasStemSibling(candidate: string): boolean;
86
+ /**
87
+ * pcic-x1vl: the entries of the in-root directory at `candidate`, by exact
88
+ * name, or why there are none to give (a file counts as `missing`).
89
+ */
90
+ entries(candidate: string): ReadonlyMap<string, fs.Dirent> | 'missing' | 'outside' | 'unreadable';
91
+ /**
92
+ * fast-glob's directory reads, routed through node:fs here so a directory the
93
+ * walk cannot list is recorded instead of silently dropping out of the result.
94
+ * The walk itself (`cwd` = root, links not followed) never leaves the root.
95
+ */
96
+ walkFs(onError: () => void): Partial<fg.FileSystemAdapter>;
97
+ }
98
+ /** A reference left unchecked because part of the tree could not be read, aggregated per evidence line. */
99
+ export type UncheckedRef = {
100
+ filePaths: Set<string>;
101
+ fileLines: Map<string, number>;
102
+ evidence: string;
103
+ };
104
+ /**
105
+ * pcic-xhzb: a directory the scan could not list is named, never turned into
106
+ * findings. Without this, an unreadable directory emptied the name index (or
107
+ * part of it) and every bare name filed there was reported as broken, with no
108
+ * sign the scan had failed. The gap is a `warning`, the severity of the
109
+ * findings it stands in for, so a `--fail-on warning` gate that a broken
110
+ * reference would have tripped is not passed silently by a scan that could
111
+ * not look (the supply-chain scan's `unscanned-files` likewise warns about
112
+ * content it could not read). Paths are repo-relative, so a hosted scan never
113
+ * shows its temp dir. Shared by the detectors that use {@link RepoTree}
114
+ * (pcic-x1vl), each with its own wording.
115
+ */
116
+ export declare function incompleteCheckIssue(tree: RepoTree, refs: UncheckedRef[], text: {
117
+ id: string;
118
+ category: IssueCategory;
119
+ title: string;
120
+ /** What was left unchecked, singular (`reference`, `command`). */
121
+ unit: string;
122
+ /** Sentence saying the outcome is unknown, without the final period. */
123
+ unknown: string;
124
+ /** The finding kind that was not reported (`broken-reference`). */
125
+ finding: string;
126
+ }): PromptCiIssue;
127
+ //# sourceMappingURL=repo-tree.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repo-tree.d.ts","sourceRoot":"","sources":["../src/repo-tree.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAE9B,OAAO,KAAK,EAAE,MAAM,WAAW,CAAC;AAChC,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAG/D;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAErD;AAED,2CAA2C;AAC3C,MAAM,MAAM,MAAM,GACd;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,GAChD;IAAE,KAAK,EAAE,SAAS,CAAA;CAAE;AACtB,yEAAyE;GACvE;IAAE,KAAK,EAAE,SAAS,CAAA;CAAE;AACtB,8EAA8E;GAC5E;IAAE,KAAK,EAAE,YAAY,CAAA;CAAE,CAAC;AAI5B,iFAAiF;AACjF,wBAAgB,SAAS,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAG1D;AAKD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,QAAQ;IACnB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,yFAAyF;IACzF,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAqB;IAC9C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA8B;IACvD,sFAAsF;IACtF,QAAQ,CAAC,UAAU,sBAA6B;gBAEpC,QAAQ,EAAE,MAAM;IAM5B,yFAAyF;IACzF,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;IAI7B,kGAAkG;IAClG,cAAc,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,GAAE,KAAK,GAAG,MAAc,GAAG,IAAI;IAS7E,0DAA0D;IAC1D,OAAO,CAAC,IAAI;IAmBZ,gFAAgF;IAChF,OAAO,CAAC,YAAY;IAKpB,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IA6CjC,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAKjC;;;OAGG;IACH,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAU1C;;;OAGG;IACH,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,WAAW,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,SAAS,GAAG,SAAS,GAAG,YAAY;IAOjG;;;;OAIG;IACH,MAAM,CAAC,OAAO,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,EAAE,CAAC,iBAAiB,CAAC;CAgB3D;AAED,2GAA2G;AAC3G,MAAM,MAAM,YAAY,GAAG;IAAE,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAAC,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAKxG;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,QAAQ,EACd,IAAI,EAAE,YAAY,EAAE,EACpB,IAAI,EAAE;IACJ,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,aAAa,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,kEAAkE;IAClE,IAAI,EAAE,MAAM,CAAC;IACb,wEAAwE;IACxE,OAAO,EAAE,MAAM,CAAC;IAChB,mEAAmE;IACnE,OAAO,EAAE,MAAM,CAAC;CACjB,GACA,aAAa,CAmCf"}
@@ -0,0 +1,263 @@
1
+ /**
2
+ * A containment-safe view of the scanned repository, for detectors that check
3
+ * whether a path an instruction file names exists (pcic-xhzb, pcic-x1vl).
4
+ *
5
+ * Instruction files are untrusted input. In the hosted product the scan root
6
+ * is a server temp directory, so an existence check on a path the text names
7
+ * (`../../etc/x`, `/etc/x`, `C:\x`, or a committed link out of the tree) would
8
+ * tell whoever wrote the scanned repository whether a server path exists.
9
+ * Every such check goes through {@link RepoTree}, which never touches a path
10
+ * outside the root.
11
+ */
12
+ import * as fs from 'node:fs';
13
+ import * as path from 'node:path';
14
+ import { isWithinRoot, realPath } from './path-containment.js';
15
+ /**
16
+ * True for a path that is absolute on ANY platform: POSIX (`/x`), Windows drive
17
+ * or UNC (`C:\x`, `\\host\share`), or drive-relative (`C:x`, which Windows
18
+ * resolves against that drive's working directory and Linux reads as a file
19
+ * name). Detectors skip these before any lookup, so a Windows and a Linux scan
20
+ * agree, the verdict never depends on where the repository is checked out, and
21
+ * a Windows scan never opens an SMB connection for `\\host\…`.
22
+ */
23
+ export function isAbsoluteAnywhere(p) {
24
+ return path.posix.isAbsolute(p) || path.win32.isAbsolute(p) || /^[A-Za-z]:/.test(p);
25
+ }
26
+ /** The `code` of a filesystem error (`EACCES`, `ENOENT`, …), when it has one. */
27
+ export function errorCode(err) {
28
+ const code = err?.code;
29
+ return typeof code === 'string' ? code : undefined;
30
+ }
31
+ /** Links followed for one lookup before it gives up (a cycle, or a chain no real tree has). */
32
+ const MAX_LINK_HOPS = 40;
33
+ /**
34
+ * pcic-xhzb: the only way a detector asks the filesystem about a path an
35
+ * instruction file names (dead references; command validity since pcic-x1vl).
36
+ *
37
+ * - Contained. A path is looked up by listing the directories from the scan
38
+ * root down to it, so nothing outside the root is ever listed, stat'ed or
39
+ * opened. A committed link is read (`readlink`, a property of the link,
40
+ * which lives in the tree) and followed only while its target stays inside
41
+ * the root; a link out of the tree makes the lookup `outside` without
42
+ * touching what it points at. A hosted scan runs against a server temp
43
+ * directory, and an existence check outside it would tell whoever wrote the
44
+ * scanned repository whether a server path exists.
45
+ * - Exact case. A segment matches a directory entry of exactly that name, so
46
+ * `Deploy_PR.yml` does not resolve to `deploy_pr.yml` on a case-insensitive
47
+ * filesystem (Windows, macOS). The result is the same on every platform, and
48
+ * the same as on Linux and GitHub, where the reference is broken.
49
+ * - Honest about failure. A directory that cannot be listed makes the lookup
50
+ * `unreadable`, never `missing`, and is recorded so the scan can say so.
51
+ *
52
+ * Listings are cached for the scan, so each directory is read at most once.
53
+ */
54
+ export class RepoTree {
55
+ root;
56
+ /** The root's real path, when it differs (a root under a symlink, like macOS `/var`). */
57
+ realRoot;
58
+ listings = new Map();
59
+ /** What could not be read, as shown in the report (`dir/` or a link's path) → why. */
60
+ unreadable = new Map();
61
+ constructor(repoRoot) {
62
+ this.root = path.resolve(repoRoot);
63
+ const real = realPath(this.root);
64
+ this.realRoot = real !== null && real !== this.root ? real : undefined;
65
+ }
66
+ /** Forward-slash path relative to the root (`.` for the root itself); never absolute. */
67
+ relative(abs) {
68
+ return path.relative(this.root, abs).split(path.sep).join('/') || '.';
69
+ }
70
+ /** Record what could not be read: a directory (shown as `dir/`) or a link (shown as its path). */
71
+ noteUnreadable(p, reason, kind = 'dir') {
72
+ const abs = path.resolve(this.root, p);
73
+ // Only in-root names are ever recorded: the report must not carry a server path.
74
+ if (!isWithinRoot(this.root, abs))
75
+ return;
76
+ const rel = this.relative(abs);
77
+ const shown = kind === 'link' ? rel : rel === '.' ? './' : `${rel}/`;
78
+ if (!this.unreadable.has(shown))
79
+ this.unreadable.set(shown, reason);
80
+ }
81
+ /** The entries of an in-root directory, by exact name. */
82
+ list(dir) {
83
+ let listing = this.listings.get(dir);
84
+ if (listing === undefined) {
85
+ try {
86
+ listing = new Map(fs.readdirSync(dir, { withFileTypes: true }).map((d) => [d.name, d]));
87
+ }
88
+ catch (err) {
89
+ const code = errorCode(err);
90
+ if (code === 'ENOENT' || code === 'ENOTDIR') {
91
+ listing = 'missing';
92
+ }
93
+ else {
94
+ listing = 'unreadable';
95
+ this.noteUnreadable(dir, code ?? 'error');
96
+ }
97
+ }
98
+ this.listings.set(dir, listing);
99
+ }
100
+ return listing;
101
+ }
102
+ /** In-root equivalent of a link target written against the root's real path. */
103
+ fromRealRoot(p) {
104
+ if (this.realRoot === undefined || isWithinRoot(this.root, p) || !isWithinRoot(this.realRoot, p))
105
+ return p;
106
+ return path.join(this.root, path.relative(this.realRoot, p));
107
+ }
108
+ lookup(candidate) {
109
+ let target = path.resolve(this.root, candidate);
110
+ let lastLink = '';
111
+ for (let hop = 0; hop <= MAX_LINK_HOPS; hop++) {
112
+ // Lexical containment first: `..`, absolute, other-drive and UNC paths stop here.
113
+ if (!isWithinRoot(this.root, target))
114
+ return { state: 'outside' };
115
+ const parts = path.relative(this.root, target).split(path.sep).filter(Boolean);
116
+ let dir = this.root;
117
+ let isDir = true;
118
+ let next;
119
+ for (let i = 0; i < parts.length; i++) {
120
+ if (!isDir)
121
+ return { state: 'missing' }; // a file in the middle of a path
122
+ const listing = this.list(dir);
123
+ if (listing === 'missing')
124
+ return { state: 'missing' };
125
+ if (listing === 'unreadable')
126
+ return { state: 'unreadable' };
127
+ const name = parts[i];
128
+ const entry = listing.get(name);
129
+ if (!entry)
130
+ return { state: 'missing' };
131
+ const entryPath = path.join(dir, name);
132
+ if (entry.isSymbolicLink()) {
133
+ let link;
134
+ try {
135
+ link = fs.readlinkSync(entryPath);
136
+ }
137
+ catch (err) {
138
+ this.noteUnreadable(entryPath, errorCode(err) ?? 'unreadable link', 'link');
139
+ return { state: 'unreadable' };
140
+ }
141
+ lastLink = entryPath;
142
+ // Resolved lexically against the link's own directory, then re-walked
143
+ // from the root: the containment check above runs before anything
144
+ // under the target is touched.
145
+ next = path.join(this.fromRealRoot(path.resolve(dir, link)), ...parts.slice(i + 1));
146
+ break;
147
+ }
148
+ isDir = entry.isDirectory();
149
+ dir = entryPath;
150
+ }
151
+ if (next === undefined)
152
+ return { state: 'found', path: dir, isDir };
153
+ target = next;
154
+ }
155
+ // More links than the kernel itself follows (Linux MAXSYMLINKS): a cycle.
156
+ this.noteUnreadable(lastLink, 'symlink loop', 'link');
157
+ return { state: 'unreadable' };
158
+ }
159
+ isDir(candidate) {
160
+ const found = this.lookup(candidate);
161
+ return found.state === 'found' && found.isDir;
162
+ }
163
+ /**
164
+ * An extension-less module path names the file that carries the extension:
165
+ * `src/lib/db` is `src/lib/db.ts`. True when the directory holds `<name>.*`.
166
+ */
167
+ hasStemSibling(candidate) {
168
+ const dir = this.lookup(path.dirname(candidate));
169
+ if (dir.state !== 'found' || !dir.isDir)
170
+ return false;
171
+ const listing = this.list(dir.path);
172
+ if (typeof listing === 'string')
173
+ return false;
174
+ const prefix = `${path.basename(candidate)}.`;
175
+ for (const name of listing.keys())
176
+ if (name.startsWith(prefix))
177
+ return true;
178
+ return false;
179
+ }
180
+ /**
181
+ * pcic-x1vl: the entries of the in-root directory at `candidate`, by exact
182
+ * name, or why there are none to give (a file counts as `missing`).
183
+ */
184
+ entries(candidate) {
185
+ const dir = this.lookup(candidate);
186
+ if (dir.state !== 'found')
187
+ return dir.state;
188
+ if (!dir.isDir)
189
+ return 'missing';
190
+ return this.list(dir.path);
191
+ }
192
+ /**
193
+ * fast-glob's directory reads, routed through node:fs here so a directory the
194
+ * walk cannot list is recorded instead of silently dropping out of the result.
195
+ * The walk itself (`cwd` = root, links not followed) never leaves the root.
196
+ */
197
+ walkFs(onError) {
198
+ const readdirSync = (dir, options) => {
199
+ try {
200
+ return options ? fs.readdirSync(dir, options) : fs.readdirSync(dir);
201
+ }
202
+ catch (err) {
203
+ const code = errorCode(err);
204
+ // A directory deleted mid-walk has nothing left to miss.
205
+ if (code !== 'ENOENT') {
206
+ this.noteUnreadable(dir, code ?? 'error');
207
+ onError();
208
+ }
209
+ throw err;
210
+ }
211
+ };
212
+ return { readdirSync: readdirSync };
213
+ }
214
+ }
215
+ /** Evidence lines listed per kind before the rest is summarised. */
216
+ const MAX_INCOMPLETE_EVIDENCE = 10;
217
+ /**
218
+ * pcic-xhzb: a directory the scan could not list is named, never turned into
219
+ * findings. Without this, an unreadable directory emptied the name index (or
220
+ * part of it) and every bare name filed there was reported as broken, with no
221
+ * sign the scan had failed. The gap is a `warning`, the severity of the
222
+ * findings it stands in for, so a `--fail-on warning` gate that a broken
223
+ * reference would have tripped is not passed silently by a scan that could
224
+ * not look (the supply-chain scan's `unscanned-files` likewise warns about
225
+ * content it could not read). Paths are repo-relative, so a hosted scan never
226
+ * shows its temp dir. Shared by the detectors that use {@link RepoTree}
227
+ * (pcic-x1vl), each with its own wording.
228
+ */
229
+ export function incompleteCheckIssue(tree, refs, text) {
230
+ const capped = (lines, noun) => lines.length > MAX_INCOMPLETE_EVIDENCE
231
+ ? [...lines.slice(0, MAX_INCOMPLETE_EVIDENCE), `…and ${lines.length - MAX_INCOMPLETE_EVIDENCE} more ${noun}`]
232
+ : lines;
233
+ const dirs = [...tree.unreadable].sort(([a], [b]) => a.localeCompare(b));
234
+ const dirLines = dirs.map(([shown, reason]) => `Could not read: ${shown} (${reason})`);
235
+ const filePaths = [...new Set(refs.flatMap((r) => [...r.filePaths]))];
236
+ const firstLine = new Map();
237
+ for (const r of refs) {
238
+ for (const [fp, line] of r.fileLines) {
239
+ const seen = firstLine.get(fp);
240
+ if (seen === undefined || line < seen)
241
+ firstLine.set(fp, line);
242
+ }
243
+ }
244
+ return {
245
+ id: text.id,
246
+ severity: 'warning',
247
+ category: text.category,
248
+ title: text.title,
249
+ summary: `${refs.length} ${text.unit}(s) were not checked because part of the repository could not be read ` +
250
+ `(${dirs.length} location${dirs.length === 1 ? '' : 's'}). ${text.unknown}; ` +
251
+ `no ${text.finding} finding was reported for them.`,
252
+ filePaths,
253
+ locations: filePaths.map((fp) => {
254
+ const startLine = firstLine.get(fp);
255
+ return startLine !== undefined ? { filePath: fp, startLine } : { filePath: fp };
256
+ }),
257
+ evidence: [...capped(dirLines, 'locations'), ...capped(refs.map((r) => r.evidence), `${text.unit}s`)],
258
+ recommendation: 'Make the listed locations readable by the user running the scan (or fix the symlink), then scan again to ' +
259
+ `check these ${text.unit}s.`,
260
+ confidence: 0.8,
261
+ };
262
+ }
263
+ //# sourceMappingURL=repo-tree.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repo-tree.js","sourceRoot":"","sources":["../src/repo-tree.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@promptci/core",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "Deterministic scanner for AI coding instruction files (CLAUDE.md, AGENTS.md, .cursorrules) — detectors, scoring, and report generation",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",