@codependix/cli 0.0.3

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,511 @@
1
+ import { BoundaryCheckService } from '@codependix/boundaries';
2
+ import { CombinedOutputService } from '@codependix/output';
3
+ import { CommandRunner } from 'nest-commander';
4
+ import { ConfigurationService } from '@codependix/configuration';
5
+ import { ConsoleLogger } from '@nestjs/common';
6
+ import { GraphRunService } from '@codependix/output';
7
+ import { MapCommandOptions } from '@codependix/configuration';
8
+ import pino from 'pino';
9
+ import { ReportingService } from '@codependix/output';
10
+ import { RunContextService } from '@codependix/boundaries';
11
+
12
+ /**
13
+ * Structured values that belong beside a log line rather than inside it.
14
+ *
15
+ * Counts, percentages, and durations are the values that change on every
16
+ * occurrence, so they are carried as fields: the message stays constant and
17
+ * groupable in telemetry, and the numbers stay queryable instead of having to
18
+ * be parsed back out of prose.
19
+ *
20
+ * The named members are the recurring ones; the index signature keeps the
21
+ * argument open for whatever a given call site needs to attach.
22
+ */
23
+ declare interface LogData {
24
+ [key: string]: unknown;
25
+ /** How many things the operation handled. */
26
+ count?: number;
27
+ /** Wall-clock milliseconds the operation took. */
28
+ durationMs?: number;
29
+ /** Completion between 0 and 100. */
30
+ percent?: number;
31
+ /** How many things the operation set out to handle. */
32
+ total?: number;
33
+ }
34
+
35
+ /**
36
+ * Transient-scoped logger so each injecting class gets its own instance.
37
+ * Each consumer calls `setContext(ClassName.name)` to tag every log line
38
+ * with the originating class. Backed by pino for structured JSON output in
39
+ * production and human-readable pretty-print in development.
40
+ *
41
+ * Messages follow one grammar: an emoji naming the subject, a verb in present
42
+ * progressive or past tense, then the object. Values that vary per call —
43
+ * counts, percentages, durations — go in the `data` argument rather than the
44
+ * message, so the message stays constant enough for telemetry to group on.
45
+ *
46
+ * ```ts
47
+ * this.logger.info("📥 Downloading CSEL sources", undefined, { total: 428 });
48
+ * this.logger.info("📥 Downloaded CSEL sources", undefined, { count: 412 });
49
+ * ```
50
+ */
51
+ declare @Injectable({ scope: Scope.TRANSIENT })
52
+ class LoggerService extends ConsoleLogger {
53
+ // 🏗 Dependency Injection
54
+
55
+ constructor() {
56
+ super();
57
+ }
58
+
59
+ // 🔐 Private Fields
60
+
61
+ private static readonly isProduction =
62
+ process.env["NODE_ENV"] === "production";
63
+
64
+ /**
65
+ * Built on first use, not when this file is evaluated.
66
+ *
67
+ * A destination fixed at import time could only ever be chosen by this
68
+ * package, since every consumer's own code runs after its imports.
69
+ */
70
+ private static rootLogger: pino.Logger | undefined;
71
+
72
+ /** Whether lines go to standard error instead of standard output. */
73
+ private static writesToStandardError = false;
74
+
75
+ private child: pino.Logger = LoggerService.root;
76
+
77
+ // 🔑 Public Fields
78
+
79
+ // 🔏 Private Methods
80
+
81
+ /** Build the pino instance for production or local development output. */
82
+ private static createRootLogger(): pino.Logger {
83
+ const level = process.env["LOG_LEVEL"] ?? "info";
84
+
85
+ if (LoggerService.isProduction) {
86
+ return LoggerService.writesToStandardError
87
+ ? pino({ level }, pino.destination(STANDARD_ERROR_DESCRIPTOR))
88
+ : pino({ level });
89
+ }
90
+
91
+ return pino({
92
+ level,
93
+ transport: {
94
+ options: {
95
+ colorize: true,
96
+ destination: LoggerService.writesToStandardError
97
+ ? STANDARD_ERROR_DESCRIPTOR
98
+ : STANDARD_OUTPUT_DESCRIPTOR,
99
+ // The emoji is a field, not part of the message, so the console can
100
+ // show it while telemetry stores unadorned prose. `ignore` then keeps
101
+ // it from being printed a second time in the trailing object.
102
+ ignore: "pid,hostname,emoji",
103
+ messageFormat: "{emoji} {msg}",
104
+ singleLine: true,
105
+ },
106
+ target: "pino-pretty",
107
+ },
108
+ });
109
+ }
110
+
111
+ /**
112
+ * Sends every subsequent line to standard error instead of standard output.
113
+ *
114
+ * For a command-line application whose standard output *is* its result. A log
115
+ * line sharing that stream is not a diagnostic beside the data, it is a
116
+ * corruption of it. Call it before anything logs — the first statement of the
117
+ * application's bootstrap.
118
+ *
119
+ * A call after the first line warns and changes nothing: the destination is
120
+ * fixed when the pino instance is built, and tearing down a transport
121
+ * somebody is writing through would be worse than refusing. The warning is
122
+ * the point — silently leaving the lines on standard output is how a caller
123
+ * would ship a corrupted pipe without ever being told.
124
+ */
125
+ static logToStandardError(): void {
126
+ if (LoggerService.rootLogger !== undefined) {
127
+ process.emitWarning(
128
+ "LoggerService.logToStandardError() was called after the first log line, so log lines still go to standard output and anything piping that stream will read them as data. Call it as the first statement of the application's bootstrap.",
129
+ );
130
+ return;
131
+ }
132
+
133
+ LoggerService.writesToStandardError = true;
134
+ }
135
+
136
+ /**
137
+ * Fails a malformed message in development, and never in production.
138
+ *
139
+ * A logger that throws in production turns an observability call into an
140
+ * outage, so the check runs only where a developer is present to fix it.
141
+ */
142
+ private assertConventionalMessage(args: {
143
+ context: string | undefined;
144
+ parsed: ParsedLogMessage;
145
+ }): void {
146
+ if (
147
+ LoggerService.isProduction ||
148
+ this.shouldSkipConventionalMessageValidation(args.context)
149
+ ) {
150
+ return;
151
+ }
152
+
153
+ const violation = this.getConventionalMessageViolation(args.parsed);
154
+
155
+ if (violation !== undefined) {
156
+ throw new Error(violation);
157
+ }
158
+ }
159
+
160
+ /** Assembles the object pino merges into the line. */
161
+ private buildBindings(args: {
162
+ context: string | undefined;
163
+ data: LogData | undefined;
164
+ parsed: ParsedLogMessage;
165
+ }): Record<string, unknown> {
166
+ this.assertConventionalMessage({
167
+ context: args.context,
168
+ parsed: args.parsed,
169
+ });
170
+
171
+ return {
172
+ ...args.data,
173
+ context: args.context,
174
+ // Telemetry gets prose; only the console-bound transport reads this.
175
+ ...(LoggerService.isProduction ? {} : { emoji: args.parsed.emoji }),
176
+ };
177
+ }
178
+
179
+ /** Returns a human-readable explanation when the message format is invalid. */
180
+ private getConventionalMessageViolation(
181
+ parsed: ParsedLogMessage,
182
+ ): string | undefined {
183
+ const emoji = parsed.emoji;
184
+ const text = parsed.text;
185
+
186
+ if (emoji === undefined) {
187
+ return `Log message must start with an emoji naming its subject, then a verb: "${text}"`;
188
+ }
189
+
190
+ const firstWord = FIRST_WORD_PATTERN.exec(text)?.[1];
191
+
192
+ if (firstWord === undefined || !this.isConventionalVerb(firstWord)) {
193
+ return `Log message must begin with a verb in present progressive or past tense, got "${firstWord ?? ""}": "${emoji} ${text}"`;
194
+ }
195
+
196
+ return undefined;
197
+ }
198
+
199
+ /**
200
+ * Whether a word is a verb in one of the two tenses the convention allows.
201
+ *
202
+ * Present progressive means the operation is under way; past means it
203
+ * finished. Regular morphology covers both, so a new verb needs no
204
+ * registration anywhere — only irregular pasts are enumerated.
205
+ */
206
+ private isConventionalVerb(word: string): boolean {
207
+ const lowercased = word.toLowerCase();
208
+
209
+ return (
210
+ lowercased.endsWith("ing") ||
211
+ lowercased.endsWith("ed") ||
212
+ IRREGULAR_PAST_VERBS.has(lowercased)
213
+ );
214
+ }
215
+
216
+ /** Splits a leading emoji off a message, leaving prose behind. */
217
+ private parseMessage(message: unknown): ParsedLogMessage {
218
+ const text = String(message);
219
+ const match = LEADING_EMOJI_PATTERN.exec(text);
220
+ const emoji = match?.[1];
221
+
222
+ return emoji === undefined
223
+ ? { emoji: undefined, text }
224
+ : { emoji, text: text.slice(match?.[0].length) };
225
+ }
226
+
227
+ /** Whether a context is intentionally exempt from the validation rule. */
228
+ private shouldSkipConventionalMessageValidation(
229
+ context: string | undefined,
230
+ ): boolean {
231
+ return context !== undefined && UNVALIDATED_LOG_CONTEXTS.has(context);
232
+ }
233
+
234
+ // 🌎 Public Methods
235
+
236
+ /** The pino instance every logger's child is taken from. */
237
+ private static get root(): pino.Logger {
238
+ LoggerService.rootLogger ??= LoggerService.createRootLogger();
239
+
240
+ return LoggerService.rootLogger;
241
+ }
242
+
243
+ /** Normalizes unknown errors into a stable message and timestamped log line. */
244
+ buildErrorLogEntry(
245
+ context: string,
246
+ error: unknown,
247
+ ): { errorMessage: string; logLine: string } {
248
+ const errorMessage =
249
+ error instanceof Error ? error.stack || error.message : String(error);
250
+
251
+ return {
252
+ errorMessage,
253
+ logLine: `[${new Date().toISOString()}] ${context}: ${errorMessage}\n`,
254
+ };
255
+ }
256
+
257
+ /** Builds a timestamped output log file path and ensures the output directory exists. */
258
+ createTimestampedOutputLogFilePath(filePrefix: string): string {
259
+ const outputDirectory = path.join(process.cwd(), "output");
260
+ if (!existsSync(outputDirectory)) {
261
+ mkdirSync(outputDirectory, { recursive: true });
262
+ }
263
+
264
+ return path.join(
265
+ outputDirectory,
266
+ `${filePrefix}-${new Date().toISOString().replaceAll(/[:.]/g, "-")}.log`,
267
+ );
268
+ }
269
+
270
+ /** Logs a debug message at the `debug` level. */
271
+ override debug(message: unknown, context?: string, data?: LogData): void {
272
+ const parsed = this.parseMessage(message);
273
+ this.child.debug(
274
+ this.buildBindings({ context: context ?? this.context, data, parsed }),
275
+ parsed.text,
276
+ );
277
+ }
278
+
279
+ /**
280
+ * Logs an error message at the `error` level, optionally including a stack trace.
281
+ *
282
+ * `ConsoleLogger.error` spends a third slot on a context string that the
283
+ * other levels do not have, so this one accepts either: a string keeps
284
+ * NestJS's meaning, an object is structured data like everywhere else.
285
+ */
286
+ override error(
287
+ message: unknown,
288
+ stackOrContext?: string,
289
+ contextOrData?: LogData | string,
290
+ ): void {
291
+ const parsed = this.parseMessage(message);
292
+ const data = typeof contextOrData === "object" ? contextOrData : undefined;
293
+ const context =
294
+ typeof contextOrData === "string" ? contextOrData : this.context;
295
+
296
+ this.child.error(
297
+ {
298
+ ...this.buildBindings({ context, data, parsed }),
299
+ stack: stackOrContext,
300
+ },
301
+ parsed.text,
302
+ );
303
+ }
304
+
305
+ /** Logs an informational message at the `info` level. */
306
+ info(message: unknown, context?: string, data?: LogData): void {
307
+ const parsed = this.parseMessage(message);
308
+ this.child.info(
309
+ this.buildBindings({ context: context ?? this.context, data, parsed }),
310
+ parsed.text,
311
+ );
312
+ }
313
+
314
+ /**
315
+ * Logs an informational message at the `info` level.
316
+ *
317
+ * NestJS and `nest-commander` call this method directly as part of the
318
+ * framework's own `LoggerService` contract, so it must keep working
319
+ * exactly as before. Application code should call `info` instead — the
320
+ * same behavior under a name that says what level it logs at.
321
+ */
322
+ override log(message: unknown, context?: string, data?: LogData): void {
323
+ this.info(message, context, data);
324
+ }
325
+
326
+ /** Sets the context label included in every subsequent log line. */
327
+ override setContext(context: string): void {
328
+ super.setContext(context);
329
+ this.child = LoggerService.root.child({ context });
330
+ }
331
+
332
+ /** Logs a verbose message at the `trace` level. */
333
+ override verbose(message: unknown, context?: string, data?: LogData): void {
334
+ const parsed = this.parseMessage(message);
335
+ this.child.trace(
336
+ this.buildBindings({ context: context ?? this.context, data, parsed }),
337
+ parsed.text,
338
+ );
339
+ }
340
+
341
+ /** Logs a warning message at the `warn` level. */
342
+ override warn(message: unknown, context?: string, data?: LogData): void {
343
+ const parsed = this.parseMessage(message);
344
+ this.child.warn(
345
+ this.buildBindings({ context: context ?? this.context, data, parsed }),
346
+ parsed.text,
347
+ );
348
+ }
349
+ }
350
+
351
+ /**
352
+ * Root NestJS application module.
353
+ */
354
+ export declare class MainModule {
355
+ }
356
+
357
+ /**
358
+ * CLI entry point for the codependix dependency graph workflow.
359
+ *
360
+ * `--write` publishes every configured export; `--check` names which finding
361
+ * fails the run — `boundaries` for an edge breaking a declared rule, `reports`
362
+ * for a configured destination that has gone stale. No per-graph-type
363
+ * subcommand: which graphs run, where each project's export lands, and which
364
+ * rules judge them is entirely a function of `codependix.config.ts`, read by
365
+ * `@codependix/configuration`.
366
+ *
367
+ * The two findings are named apart because they belong on opposite sides of a
368
+ * pull request. An export moves with the workspace it describes, so gating
369
+ * staleness on a branch fails every branch that touched a project graph; a
370
+ * broken boundary is caused by the branch and fixed by it. This is the split
371
+ * `callidescope` already made between `--check depth` and `--check reports`,
372
+ * copied wholesale down to the spelling of `reports`.
373
+ */
374
+ export declare class MapCommand extends CommandRunner {
375
+ private readonly graphRunService;
376
+ private readonly boundaryCheckService;
377
+ private readonly combinedOutputService;
378
+ private readonly configurationService;
379
+ private readonly logger;
380
+ private readonly reportingService;
381
+ private readonly runContextService;
382
+ constructor(graphRunService: GraphRunService, boundaryCheckService: BoundaryCheckService, combinedOutputService: CombinedOutputService, configurationService: ConfigurationService, logger: LoggerService, reportingService: ReportingService, runContextService: RunContextService);
383
+ /**
384
+ * Prints and writes every active graph type's combined output, when the
385
+ * export pass ran at all.
386
+ *
387
+ * A `--check boundaries`-only run never reaches this: nothing was built to
388
+ * combine, so `--format`/`--json-output`/`--markdown-output` are silently
389
+ * inert on a run that touched no export at all — the same way `--write`
390
+ * and `--check reports` are inert on one that never named a destination.
391
+ */
392
+ private runCombinedOutput;
393
+ /**
394
+ * Runs the export pass, warning when nothing was selected.
395
+ *
396
+ * Returns both the usual delivery outcome and every active graph type's
397
+ * whole-workspace data, so `runMode` can hand the latter to
398
+ * `CombinedOutputService` without running the export pass a second time.
399
+ */
400
+ private runExports;
401
+ /**
402
+ * Runs the passes a resolved mode selected, and reports what they found.
403
+ *
404
+ * Split from `run` so the command line's own rejection path stays a
405
+ * handful of lines: everything below here has a mode to act on. Both
406
+ * passes are run directly here, rather than through a further-nested
407
+ * helper, to keep `MapCommand.run`'s own call stack inside this project's
408
+ * callidescope depth limit — see `packages/codependix-cli/callidescope.config.ts`.
409
+ */
410
+ private runMode;
411
+ /**
412
+ * Parses the set of findings the run fails on.
413
+ *
414
+ * The parser runs only when `--check` carries a value, so anything reaching
415
+ * it is a written set. A `--check` with no value never arrives here and is
416
+ * refused later: a set with nothing in it is indistinguishable from the flag
417
+ * having been left off, which is how one flag came to gate two findings.
418
+ */
419
+ parseCheck(value: string): string;
420
+ /** Parses the optional configuration path from command-line input. */
421
+ parseConfig(value: string | undefined): string | undefined;
422
+ /** Parses the directory whose Nx workspace this run reads. */
423
+ parseDirectory(value: string | undefined): string;
424
+ /**
425
+ * Parses `--exclude`, a comma-separated list of globs overriding the
426
+ * configured `exclude`.
427
+ *
428
+ * Refused later, by `ConfigurationService.loadConfiguration`, when the
429
+ * configuration this run reads never declared `exclude` in the first
430
+ * place — matching callidescope's `--exclude` refusal exactly.
431
+ */
432
+ parseExclude(value: string | undefined): string[];
433
+ /** Enables the `fileImports` graph type for this run. */
434
+ parseFileImports(): true;
435
+ /**
436
+ * Parses what `--format` prints to standard output.
437
+ *
438
+ * Defaults to Markdown when the flag was left off entirely — see
439
+ * `MapCommand.resolveFormat`, which validates the value this returns.
440
+ */
441
+ parseFormat(value: string | undefined): string | undefined;
442
+ /**
443
+ * Parses `--include`, a comma-separated list of globs overriding the
444
+ * configured `include`.
445
+ *
446
+ * Refused later, alongside `--exclude`, when the configuration this run
447
+ * reads never declared `include`.
448
+ */
449
+ parseInclude(value: string | undefined): string[];
450
+ /**
451
+ * Parses `--json-output`, the path to write every active graph type's
452
+ * combined JSON data to, keyed by graph type name.
453
+ */
454
+ parseJsonOutput(value: string | undefined): string | undefined;
455
+ /**
456
+ * Parses `--markdown-output`, the path to write every active graph type's
457
+ * combined, anchor-spliced Markdown diagram to.
458
+ */
459
+ parseMarkdownOutput(value: string | undefined): string | undefined;
460
+ /** Enables the `nestjsModules` graph type for this run. */
461
+ parseNestjsModules(): true;
462
+ /** Disables the `fileImports` graph type for this run. */
463
+ parseNoFileImports(): false;
464
+ /** Disables the `nestjsModules` graph type for this run. */
465
+ parseNoNestjsModules(): false;
466
+ /** Disables the `nxProjects` graph type for this run. */
467
+ parseNoNxProjects(): false;
468
+ /** Enables the `nxProjects` graph type for this run. */
469
+ parseNxProjects(): true;
470
+ /**
471
+ * Parses the projects a run exports for beyond `include`.
472
+ *
473
+ * **Widening, and narrowing.** A named project is added to whatever
474
+ * `include` already selected, and `exclude` still wins over it. It also
475
+ * narrows what a run draws and judges: the Workspace Graph's node set and
476
+ * every level `--check boundaries` judges become the named set. A narrowed
477
+ * gate sees fewer edges than a whole-workspace one — fine for a local run,
478
+ * and worth thinking twice about in CI.
479
+ */
480
+ parseProjects(value: string | undefined): string | undefined;
481
+ /**
482
+ * Parses the Nx tags a run exports for, matched exactly against a project's
483
+ * own tags. Widens and narrows exactly as `--projects` does.
484
+ */
485
+ parseTags(value: string | undefined): string | undefined;
486
+ /** Parses the `--write` flag from command-line input. */
487
+ parseWrite(value: boolean | undefined): boolean;
488
+ /**
489
+ * Runs whatever the command line asked for: exports, boundaries, or both.
490
+ *
491
+ * Every project is attempted regardless of whether an earlier one failed —
492
+ * both passes isolate a project's failure to itself — so this only decides
493
+ * the exit code from what came back. The two passes are also weighed
494
+ * independently rather than the first failure short-circuiting the second:
495
+ * a run gating both should report both, not the one that happened to run
496
+ * first.
497
+ */
498
+ run(_passedParameters: string[], options?: MapCommandOptions): Promise<void>;
499
+ }
500
+
501
+ /** Wires the codependix CLI command together with its collaborators. */
502
+ export declare class MapModule {
503
+ }
504
+
505
+ /** A message split into the emoji the console shows and the prose telemetry stores. */
506
+ declare interface ParsedLogMessage {
507
+ emoji: string | undefined;
508
+ text: string;
509
+ }
510
+
511
+ export { }
@@ -0,0 +1,2 @@
1
+ import { n as e, r as t, t as n } from "../main.module-B07sXqnE.js";
2
+ export { n as MainModule, t as MapCommand, e as MapModule };
@@ -0,0 +1 @@
1
+ export {}
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env node
2
+ import { i as e, t } from "../main.module-B07sXqnE.js";
3
+ import { CommandFactory as n } from "nest-commander";
4
+ import "reflect-metadata";
5
+ //#region packages/ic-suite/codependix/codependix-cli/src/main.ts
6
+ async function r() {
7
+ e.logToStandardError();
8
+ let r = new e();
9
+ r.setContext("CommandFactory"), await n.run(t, {
10
+ bufferLogs: !0,
11
+ logger: r
12
+ });
13
+ }
14
+ r();
15
+ //#endregion
package/package.json ADDED
@@ -0,0 +1,77 @@
1
+ {
2
+ "author": "Jimmy Paolini",
3
+ "bin": {
4
+ "codependix": "./dist/src/main.js"
5
+ },
6
+ "bugs": {
7
+ "url": "https://github.com/organizzolini/codebase/issues"
8
+ },
9
+ "dependencies": {
10
+ "@codependix/boundaries": "0.0.3",
11
+ "@codependix/configuration": "0.0.3",
12
+ "@codependix/core": "0.0.3",
13
+ "@codependix/output": "0.0.3",
14
+ "@nestjs/common": "^11.1.21 || ^12.0.1",
15
+ "@nestjs/config": "^12.0.0",
16
+ "@nestjs/core": "^11.1.21 || ^12.0.1",
17
+ "nest-commander": "^3.21.0",
18
+ "pino": "^10.3.1",
19
+ "pino-pretty": "^13.1.3",
20
+ "reflect-metadata": "^0.2.2",
21
+ "zod": "^4.4.3"
22
+ },
23
+ "description": "Command-line tool for exporting and validating Nx, NestJS, and file-level dependency graphs and boundaries",
24
+ "devDependencies": {
25
+ "@codebase/logger": "0.0.1",
26
+ "@golevelup/ts-vitest": "^4.0.0",
27
+ "@nestjs/testing": "^12.0.1",
28
+ "@nx/eslint-plugin": "^23.0.0",
29
+ "@swc-node/register": "^1.11.1",
30
+ "@swc/core": "^1.15.40",
31
+ "@swc/helpers": "^0.5.21",
32
+ "@types/node": "^26.5.1",
33
+ "@vitest/coverage-v8": "^5.0.0",
34
+ "typescript": "^6.0.3",
35
+ "vitest": "^5.0.0"
36
+ },
37
+ "exports": {
38
+ ".": {
39
+ "types": "./dist/src/index.d.ts",
40
+ "default": "./dist/src/index.js"
41
+ },
42
+ "./package.json": "./package.json"
43
+ },
44
+ "files": [
45
+ "dist",
46
+ "LICENSE",
47
+ "README.md"
48
+ ],
49
+ "homepage": "https://github.com/organizzolini/codebase/tree/main/packages/ic-suite/codependix/codependix-cli#readme",
50
+ "keywords": [
51
+ "architecture-linter",
52
+ "boundaries",
53
+ "cli",
54
+ "codependix",
55
+ "dependency-graph",
56
+ "mermaid"
57
+ ],
58
+ "license": "MIT",
59
+ "main": "./dist/src/index.js",
60
+ "name": "@codependix/cli",
61
+ "publishConfig": {
62
+ "access": "public",
63
+ "registry": "https://registry.npmjs.org"
64
+ },
65
+ "repository": {
66
+ "directory": "packages/ic-suite/codependix/codependix-cli",
67
+ "type": "git",
68
+ "url": "git+https://github.com/organizzolini/codebase.git"
69
+ },
70
+ "type": "module",
71
+ "typeCoverage": {
72
+ "atLeast": 100,
73
+ "strict": true
74
+ },
75
+ "types": "./dist/src/index.d.ts",
76
+ "version": "0.0.3"
77
+ }