@theholocron/holocron-plugin-github 2.0.0-alpha.1 → 2.0.0-alpha.5
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/dist/index.d.mts +54 -4
- package/dist/index.mjs +111 -19
- package/package.json +6 -4
package/dist/index.d.mts
CHANGED
|
@@ -4,10 +4,13 @@ import { Auth, Ci, CiRun, CiRunFilter, Environment, Environments, Issue, IssueSe
|
|
|
4
4
|
/**
|
|
5
5
|
* Token resolution for the GitHub plugin. See README §Auth.
|
|
6
6
|
*
|
|
7
|
-
* Resolution order
|
|
7
|
+
* Resolution order (matches the standard 4-step precedence set by
|
|
8
|
+
* `.notes/tech-auth-bootstrap.spec.md`):
|
|
8
9
|
* 1. explicit `token` argument (from `--token` flag)
|
|
9
10
|
* 2. HOLOCRON_GH_TOKEN env var (preferred over GITHUB_TOKEN — clearer intent)
|
|
10
11
|
* 3. GITHUB_TOKEN env var (auto-injected in GH Actions)
|
|
12
|
+
* 4. keyring (com.theholocron.cli / "github")
|
|
13
|
+
* 5. AuthError naming all four options + the bootstrap hint
|
|
11
14
|
*
|
|
12
15
|
* No `gh auth token` fallback by design — it usually has narrower
|
|
13
16
|
* scopes than admin commands need.
|
|
@@ -20,6 +23,8 @@ interface ResolveTokenInput {
|
|
|
20
23
|
cliToken?: string;
|
|
21
24
|
/** Env vars; passed in for testability. Defaults to `process.env`. */
|
|
22
25
|
env?: NodeJS.ProcessEnv;
|
|
26
|
+
/** Keyring lookup fn; passed in for testability. Defaults to `getToken(provider)`. */
|
|
27
|
+
keyring?: (provider: string) => string | null;
|
|
23
28
|
}
|
|
24
29
|
declare function resolveToken(input?: ResolveTokenInput): string;
|
|
25
30
|
//#endregion
|
|
@@ -73,6 +78,7 @@ declare class GitHubSource implements Source {
|
|
|
73
78
|
private readonly owner;
|
|
74
79
|
private readonly name;
|
|
75
80
|
private readonly repoPath;
|
|
81
|
+
private readonly repoRoot;
|
|
76
82
|
private readonly workflowDir;
|
|
77
83
|
constructor(rest: GitHubRestClient, opts: SourceOptions);
|
|
78
84
|
whoami(): Promise<{
|
|
@@ -83,14 +89,18 @@ declare class GitHubSource implements Source {
|
|
|
83
89
|
createRuleset(payload: Record<string, unknown>): Promise<Ruleset>;
|
|
84
90
|
updateRuleset(id: number, payload: Record<string, unknown>): Promise<Ruleset>;
|
|
85
91
|
updateRepoSettings(settings: RepoSettings): Promise<void>;
|
|
92
|
+
protectBranch(branch: string, payload: Record<string, unknown>): Promise<void>;
|
|
86
93
|
enableVulnerabilityAlerts(): Promise<void>;
|
|
87
94
|
enableAutomatedSecurityFixes(): Promise<void>;
|
|
88
95
|
enableSecretScanning(): Promise<void>;
|
|
96
|
+
enableCodeScanning(): Promise<string>;
|
|
97
|
+
enableDependencyGraph(): Promise<void>;
|
|
89
98
|
enablePrivateVulnerabilityReporting(): Promise<void>;
|
|
90
99
|
listWorkflowFiles(): Promise<string[]>;
|
|
91
100
|
readWorkflowFile(name: string): Promise<string | null>;
|
|
92
101
|
writeWorkflowFile(name: string, contents: string): Promise<void>;
|
|
93
102
|
removeWorkflowFile(name: string): Promise<void>;
|
|
103
|
+
writeRepoFile(path: string, contents: string): Promise<void>;
|
|
94
104
|
}
|
|
95
105
|
//#endregion
|
|
96
106
|
//#region src/capabilities/secrets.d.ts
|
|
@@ -151,8 +161,15 @@ declare class GitHubCi implements Ci {
|
|
|
151
161
|
//#region src/capabilities/issues.d.ts
|
|
152
162
|
interface IssuesOptions {
|
|
153
163
|
repo: string;
|
|
154
|
-
/**
|
|
155
|
-
|
|
164
|
+
/**
|
|
165
|
+
* Lifecycle slot → status label name. Optional at construction; if
|
|
166
|
+
* omitted, capability methods that need labels (`transition`,
|
|
167
|
+
* `doctor`) degrade gracefully — `transition` throws a clear error,
|
|
168
|
+
* `doctor` reports the two label-backed slots as unresolved. This
|
|
169
|
+
* lets the plugin load in configs where the operator hasn't picked
|
|
170
|
+
* labels yet without failing the whole runtime.
|
|
171
|
+
*/
|
|
172
|
+
labels?: {
|
|
156
173
|
inProgress: string;
|
|
157
174
|
inReview: string;
|
|
158
175
|
};
|
|
@@ -185,6 +202,32 @@ declare class GitHubIssues implements Issues {
|
|
|
185
202
|
private mapIssue;
|
|
186
203
|
}
|
|
187
204
|
//#endregion
|
|
205
|
+
//#region src/verify-token.d.ts
|
|
206
|
+
/**
|
|
207
|
+
* `verifyToken` — plugin-level export used by `holocron auth set` +
|
|
208
|
+
* `holocron auth check`. Hits `GET /user` and translates the response
|
|
209
|
+
* into the normalized `VerifyTokenResult` shape.
|
|
210
|
+
*
|
|
211
|
+
* Kept as a standalone function (not a capability method) so the auth
|
|
212
|
+
* command can call it without initializing the full plugin — plugin
|
|
213
|
+
* construction requires an already-resolved token, which is exactly
|
|
214
|
+
* what we don't have yet at bootstrap time.
|
|
215
|
+
*/
|
|
216
|
+
interface VerifyTokenSuccess {
|
|
217
|
+
ok: true;
|
|
218
|
+
subject: string;
|
|
219
|
+
}
|
|
220
|
+
interface VerifyTokenFailure {
|
|
221
|
+
ok: false;
|
|
222
|
+
message: string;
|
|
223
|
+
}
|
|
224
|
+
type VerifyTokenResult = VerifyTokenSuccess | VerifyTokenFailure;
|
|
225
|
+
interface VerifyTokenOptions {
|
|
226
|
+
baseUrl?: string;
|
|
227
|
+
fetch?: typeof fetch;
|
|
228
|
+
}
|
|
229
|
+
declare function verifyToken(token: string, opts?: VerifyTokenOptions): Promise<VerifyTokenResult>;
|
|
230
|
+
//#endregion
|
|
188
231
|
//#region src/index.d.ts
|
|
189
232
|
interface GitHubPluginOptions extends ResolveTokenInput {
|
|
190
233
|
/** "owner/name" — e.g., "theholocron/holocron". Required. */
|
|
@@ -224,5 +267,12 @@ declare function createPlugin(options: GitHubPluginOptions): {
|
|
|
224
267
|
issues: () => Issues;
|
|
225
268
|
};
|
|
226
269
|
};
|
|
270
|
+
/**
|
|
271
|
+
* One-line hint printed by `holocron auth set github` when no token
|
|
272
|
+
* is supplied or the supplied token is rejected. Generate a PAT at
|
|
273
|
+
* https://github.com/settings/tokens with `repo` + `admin:repo_hook`
|
|
274
|
+
* scopes (add `admin:org` for org-level operations).
|
|
275
|
+
*/
|
|
276
|
+
declare const AUTH_HINT: string;
|
|
227
277
|
//#endregion
|
|
228
|
-
export { type Auth, AuthError, GitHubCi, GitHubEnvironments, GitHubIssues, GitHubPluginOptions, GitHubRestClient, GitHubSecrets, GitHubSource, PluginContext, ResolveTokenInput, ci, createContext, createPlugin, encryptSecret, environments, issues, resolveToken, secrets, source };
|
|
278
|
+
export { AUTH_HINT, type Auth, AuthError, GitHubCi, GitHubEnvironments, GitHubIssues, GitHubPluginOptions, GitHubRestClient, GitHubSecrets, GitHubSource, PluginContext, ResolveTokenInput, type VerifyTokenFailure, type VerifyTokenResult, type VerifyTokenSuccess, ci, createContext, createPlugin, encryptSecret, environments, issues, resolveToken, secrets, source, verifyToken };
|
package/dist/index.mjs
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
|
+
import { ProviderApiError, getToken } from "@theholocron/cli";
|
|
2
3
|
import { mkdir, readFile, readdir, rm, stat, writeFile } from "node:fs/promises";
|
|
3
|
-
import { join } from "node:path";
|
|
4
|
-
import { ProviderApiError } from "@theholocron/cli";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
5
|
//#region src/auth.ts
|
|
6
6
|
/**
|
|
7
7
|
* Token resolution for the GitHub plugin. See README §Auth.
|
|
8
8
|
*
|
|
9
|
-
* Resolution order
|
|
9
|
+
* Resolution order (matches the standard 4-step precedence set by
|
|
10
|
+
* `.notes/tech-auth-bootstrap.spec.md`):
|
|
10
11
|
* 1. explicit `token` argument (from `--token` flag)
|
|
11
12
|
* 2. HOLOCRON_GH_TOKEN env var (preferred over GITHUB_TOKEN — clearer intent)
|
|
12
13
|
* 3. GITHUB_TOKEN env var (auto-injected in GH Actions)
|
|
14
|
+
* 4. keyring (com.theholocron.cli / "github")
|
|
15
|
+
* 5. AuthError naming all four options + the bootstrap hint
|
|
13
16
|
*
|
|
14
17
|
* No `gh auth token` fallback by design — it usually has narrower
|
|
15
18
|
* scopes than admin commands need.
|
|
@@ -19,8 +22,9 @@ var AuthError = class extends Error {
|
|
|
19
22
|
};
|
|
20
23
|
function resolveToken(input = {}) {
|
|
21
24
|
const env = input.env ?? process.env;
|
|
22
|
-
const
|
|
23
|
-
|
|
25
|
+
const keyring = input.keyring ?? getToken;
|
|
26
|
+
const token = input.cliToken || env.HOLOCRON_GH_TOKEN || env.GITHUB_TOKEN || keyring("github");
|
|
27
|
+
if (!token) throw new AuthError("no GitHub token found. Pass --token <PAT>, set HOLOCRON_GH_TOKEN / GITHUB_TOKEN, or run: holocron auth set github <PAT>");
|
|
24
28
|
return token;
|
|
25
29
|
}
|
|
26
30
|
//#endregion
|
|
@@ -214,6 +218,7 @@ var GitHubIssues = class {
|
|
|
214
218
|
via: "closed (completed)"
|
|
215
219
|
};
|
|
216
220
|
}
|
|
221
|
+
if (!this.labels) throw new Error(`transition to \`${slot}\` requires \`labels\` in plugin options: { inProgress: '<label>', inReview: '<label>' }`);
|
|
217
222
|
const targetLabel = slot === "inProgress" ? this.labels.inProgress : this.labels.inReview;
|
|
218
223
|
const alreadyOpen = issue.state === "open";
|
|
219
224
|
const alreadyLabeled = currentStatusLabels.includes(targetLabel);
|
|
@@ -255,20 +260,19 @@ var GitHubIssues = class {
|
|
|
255
260
|
name: "open (no status label)",
|
|
256
261
|
category: "open"
|
|
257
262
|
},
|
|
258
|
-
{
|
|
263
|
+
...this.labels ? [{
|
|
259
264
|
name: `open + ${this.labels.inProgress}`,
|
|
260
265
|
category: "in-progress"
|
|
261
|
-
},
|
|
262
|
-
{
|
|
266
|
+
}, {
|
|
263
267
|
name: `open + ${this.labels.inReview}`,
|
|
264
268
|
category: "in-review"
|
|
265
|
-
},
|
|
269
|
+
}] : [],
|
|
266
270
|
{
|
|
267
271
|
name: "closed",
|
|
268
272
|
category: "done"
|
|
269
273
|
}
|
|
270
274
|
];
|
|
271
|
-
const lifecycle = [
|
|
275
|
+
const lifecycle = this.labels ? [
|
|
272
276
|
{
|
|
273
277
|
slot: "inProgress",
|
|
274
278
|
value: this.labels.inProgress,
|
|
@@ -287,6 +291,25 @@ var GitHubIssues = class {
|
|
|
287
291
|
resolved: true,
|
|
288
292
|
note: "(intrinsic — GitHub close-with-reason)"
|
|
289
293
|
}
|
|
294
|
+
] : [
|
|
295
|
+
{
|
|
296
|
+
slot: "inProgress",
|
|
297
|
+
value: null,
|
|
298
|
+
resolved: false,
|
|
299
|
+
note: "no `labels` configured in plugin options — transitions to `inProgress` will error"
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
slot: "inReview",
|
|
303
|
+
value: null,
|
|
304
|
+
resolved: false,
|
|
305
|
+
note: "no `labels` configured in plugin options — transitions to `inReview` will error"
|
|
306
|
+
},
|
|
307
|
+
{
|
|
308
|
+
slot: "done",
|
|
309
|
+
value: "closed (state_reason=completed)",
|
|
310
|
+
resolved: true,
|
|
311
|
+
note: "(intrinsic — GitHub close-with-reason)"
|
|
312
|
+
}
|
|
290
313
|
];
|
|
291
314
|
return {
|
|
292
315
|
authedAs: `${me.displayName} (${me.emailAddress ?? me.id})`,
|
|
@@ -309,10 +332,10 @@ var GitHubIssues = class {
|
|
|
309
332
|
let category = "open";
|
|
310
333
|
let statusName = raw.state === "closed" ? "closed" : "open";
|
|
311
334
|
if (raw.state === "closed") category = "done";
|
|
312
|
-
else if (statusLabels.includes(this.labels.inProgress)) {
|
|
335
|
+
else if (this.labels && statusLabels.includes(this.labels.inProgress)) {
|
|
313
336
|
category = "in-progress";
|
|
314
337
|
statusName = `open + ${this.labels.inProgress}`;
|
|
315
|
-
} else if (statusLabels.includes(this.labels.inReview)) {
|
|
338
|
+
} else if (this.labels && statusLabels.includes(this.labels.inReview)) {
|
|
316
339
|
category = "in-review";
|
|
317
340
|
statusName = `open + ${this.labels.inReview}`;
|
|
318
341
|
}
|
|
@@ -449,6 +472,7 @@ var GitHubSource = class {
|
|
|
449
472
|
owner;
|
|
450
473
|
name;
|
|
451
474
|
repoPath;
|
|
475
|
+
repoRoot;
|
|
452
476
|
workflowDir;
|
|
453
477
|
constructor(rest, opts) {
|
|
454
478
|
this.rest = rest;
|
|
@@ -456,6 +480,7 @@ var GitHubSource = class {
|
|
|
456
480
|
this.owner = owner;
|
|
457
481
|
this.name = name;
|
|
458
482
|
this.repoPath = `/repos/${owner}/${name}`;
|
|
483
|
+
this.repoRoot = opts.repoRoot;
|
|
459
484
|
this.workflowDir = join(opts.repoRoot, ".github", "workflows");
|
|
460
485
|
}
|
|
461
486
|
async whoami() {
|
|
@@ -490,6 +515,12 @@ var GitHubSource = class {
|
|
|
490
515
|
body: settings
|
|
491
516
|
});
|
|
492
517
|
}
|
|
518
|
+
async protectBranch(branch, payload) {
|
|
519
|
+
await this.rest.request(`${this.repoPath}/branches/${encodeURIComponent(branch)}/protection`, {
|
|
520
|
+
method: "PUT",
|
|
521
|
+
body: payload
|
|
522
|
+
});
|
|
523
|
+
}
|
|
493
524
|
async enableVulnerabilityAlerts() {
|
|
494
525
|
await this.rest.request(`${this.repoPath}/vulnerability-alerts`, {
|
|
495
526
|
method: "PUT",
|
|
@@ -507,7 +538,28 @@ var GitHubSource = class {
|
|
|
507
538
|
method: "PATCH",
|
|
508
539
|
body: { security_and_analysis: {
|
|
509
540
|
secret_scanning: { status: "enabled" },
|
|
510
|
-
secret_scanning_push_protection: { status: "enabled" }
|
|
541
|
+
secret_scanning_push_protection: { status: "enabled" },
|
|
542
|
+
secret_scanning_validity_checks: { status: "enabled" },
|
|
543
|
+
secret_scanning_non_provider_patterns: { status: "enabled" }
|
|
544
|
+
} }
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
async enableCodeScanning() {
|
|
548
|
+
return `run ${(await this.rest.request(`${this.repoPath}/code-scanning/default-setup`, {
|
|
549
|
+
method: "PATCH",
|
|
550
|
+
body: {
|
|
551
|
+
state: "configured",
|
|
552
|
+
query_suite: "extended",
|
|
553
|
+
threat_model: "remote_and_local"
|
|
554
|
+
}
|
|
555
|
+
})).run_id}`;
|
|
556
|
+
}
|
|
557
|
+
async enableDependencyGraph() {
|
|
558
|
+
await this.rest.request(this.repoPath, {
|
|
559
|
+
method: "PATCH",
|
|
560
|
+
body: { security_and_analysis: {
|
|
561
|
+
dependency_graph: { status: "enabled" },
|
|
562
|
+
dependency_graph_autosubmit_action: { status: "enabled" }
|
|
511
563
|
} }
|
|
512
564
|
});
|
|
513
565
|
}
|
|
@@ -545,6 +597,11 @@ var GitHubSource = class {
|
|
|
545
597
|
throw err;
|
|
546
598
|
}
|
|
547
599
|
}
|
|
600
|
+
async writeRepoFile(path, contents) {
|
|
601
|
+
const full = join(this.repoRoot, path);
|
|
602
|
+
await ensureDir(dirname(full));
|
|
603
|
+
await writeFile(full, contents, "utf8");
|
|
604
|
+
}
|
|
548
605
|
};
|
|
549
606
|
function isENOENT(err) {
|
|
550
607
|
return typeof err === "object" && err !== null && "code" in err && err.code === "ENOENT";
|
|
@@ -610,6 +667,36 @@ var GitHubRestClient = class {
|
|
|
610
667
|
}
|
|
611
668
|
};
|
|
612
669
|
//#endregion
|
|
670
|
+
//#region src/verify-token.ts
|
|
671
|
+
/**
|
|
672
|
+
* `verifyToken` — plugin-level export used by `holocron auth set` +
|
|
673
|
+
* `holocron auth check`. Hits `GET /user` and translates the response
|
|
674
|
+
* into the normalized `VerifyTokenResult` shape.
|
|
675
|
+
*
|
|
676
|
+
* Kept as a standalone function (not a capability method) so the auth
|
|
677
|
+
* command can call it without initializing the full plugin — plugin
|
|
678
|
+
* construction requires an already-resolved token, which is exactly
|
|
679
|
+
* what we don't have yet at bootstrap time.
|
|
680
|
+
*/
|
|
681
|
+
async function verifyToken(token, opts = {}) {
|
|
682
|
+
const restOpts = { token };
|
|
683
|
+
if (opts.baseUrl !== void 0) restOpts.baseUrl = opts.baseUrl;
|
|
684
|
+
if (opts.fetch !== void 0) restOpts.fetch = opts.fetch;
|
|
685
|
+
const rest = new GitHubRestClient(restOpts);
|
|
686
|
+
try {
|
|
687
|
+
const me = await rest.request("/user");
|
|
688
|
+
return {
|
|
689
|
+
ok: true,
|
|
690
|
+
subject: `user @ ${me.login ?? me.email ?? "unknown"}`
|
|
691
|
+
};
|
|
692
|
+
} catch (err) {
|
|
693
|
+
return {
|
|
694
|
+
ok: false,
|
|
695
|
+
message: err instanceof Error ? err.message : String(err)
|
|
696
|
+
};
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
//#endregion
|
|
613
700
|
//#region src/index.ts
|
|
614
701
|
function createContext(options) {
|
|
615
702
|
return {
|
|
@@ -639,11 +726,9 @@ function ci(ctx) {
|
|
|
639
726
|
return new GitHubCi(ctx.rest, { repo: ctx.repo });
|
|
640
727
|
}
|
|
641
728
|
function issues(ctx) {
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
labels: ctx.options.labels
|
|
646
|
-
});
|
|
729
|
+
const opts = { repo: ctx.repo };
|
|
730
|
+
if (ctx.options.labels !== void 0) opts.labels = ctx.options.labels;
|
|
731
|
+
return new GitHubIssues(ctx.rest, opts);
|
|
647
732
|
}
|
|
648
733
|
function createPlugin(options) {
|
|
649
734
|
const ctx = createContext(options);
|
|
@@ -658,5 +743,12 @@ function createPlugin(options) {
|
|
|
658
743
|
}
|
|
659
744
|
};
|
|
660
745
|
}
|
|
746
|
+
/**
|
|
747
|
+
* One-line hint printed by `holocron auth set github` when no token
|
|
748
|
+
* is supplied or the supplied token is rejected. Generate a PAT at
|
|
749
|
+
* https://github.com/settings/tokens with `repo` + `admin:repo_hook`
|
|
750
|
+
* scopes (add `admin:org` for org-level operations).
|
|
751
|
+
*/
|
|
752
|
+
const AUTH_HINT = "generate a Personal Access Token at https://github.com/settings/tokens (scopes: repo, admin:repo_hook — plus admin:org for org-level ops), then run: holocron auth set github <PAT>";
|
|
661
753
|
//#endregion
|
|
662
|
-
export { AuthError, GitHubCi, GitHubEnvironments, GitHubIssues, GitHubRestClient, GitHubSecrets, GitHubSource, ci, createContext, createPlugin, encryptSecret, environments, issues, resolveToken, secrets, source };
|
|
754
|
+
export { AUTH_HINT, AuthError, GitHubCi, GitHubEnvironments, GitHubIssues, GitHubRestClient, GitHubSecrets, GitHubSource, ci, createContext, createPlugin, encryptSecret, environments, issues, resolveToken, secrets, source, verifyToken };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@theholocron/holocron-plugin-github",
|
|
3
|
-
"version": "2.0.0-alpha.
|
|
3
|
+
"version": "2.0.0-alpha.5",
|
|
4
4
|
"description": "Holocron plugin for GitHub. Implements source, ci, secrets, environments, and issues capabilities against the GitHub REST API.",
|
|
5
5
|
"homepage": "https://github.com/theholocron/holocron/tree/main/packages/holocron-plugin-github#readme",
|
|
6
6
|
"bugs": "https://github.com/theholocron/holocron/issues",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
}
|
|
22
22
|
},
|
|
23
23
|
"peerDependencies": {
|
|
24
|
-
"@theholocron/cli": "2.0.0-alpha.
|
|
24
|
+
"@theholocron/cli": "2.0.0-alpha.5"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"libsodium-wrappers": "^0.7.15"
|
|
@@ -37,7 +37,8 @@
|
|
|
37
37
|
"typescript": "^5.9.3",
|
|
38
38
|
"vitest": "^3.2.6",
|
|
39
39
|
"tsdown": "^0.22.3",
|
|
40
|
-
"
|
|
40
|
+
"tsx": "^4.22.4",
|
|
41
|
+
"@theholocron/cli": "2.0.0-alpha.5"
|
|
41
42
|
},
|
|
42
43
|
"publishConfig": {
|
|
43
44
|
"access": "public"
|
|
@@ -52,7 +53,8 @@
|
|
|
52
53
|
"typecheck": "tsc --noEmit",
|
|
53
54
|
"test": "vitest run",
|
|
54
55
|
"test:watch": "vitest",
|
|
55
|
-
"test:coverage": "vitest run --coverage"
|
|
56
|
+
"test:coverage": "vitest run --coverage",
|
|
57
|
+
"validate": "tsx scripts/validate.mjs"
|
|
56
58
|
},
|
|
57
59
|
"types": "./dist/index.d.mts"
|
|
58
60
|
}
|