patram 0.0.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.
- package/LICENSE +21 -0
- package/bin/patram.js +169 -0
- package/bin/patram.test.js +184 -0
- package/lib/build-graph.js +222 -0
- package/lib/build-graph.test.js +141 -0
- package/lib/build-graph.types.ts +23 -0
- package/lib/check-graph.js +125 -0
- package/lib/check-graph.test.js +103 -0
- package/lib/list-source-files.js +44 -0
- package/lib/list-source-files.test.js +101 -0
- package/lib/load-patram-config.js +244 -0
- package/lib/load-patram-config.test.js +211 -0
- package/lib/load-patram-config.types.ts +23 -0
- package/lib/parse-claims.js +178 -0
- package/lib/parse-claims.test.js +113 -0
- package/lib/parse-claims.types.ts +27 -0
- package/lib/patram-config.js +194 -0
- package/lib/patram-config.test.js +147 -0
- package/lib/patram-config.types.ts +33 -0
- package/package.json +64 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { expect, it } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { parseClaims } from './parse-claims.js';
|
|
4
|
+
|
|
5
|
+
it('extracts markdown title, links and directives as neutral claims', () => {
|
|
6
|
+
const claims = parseClaims({
|
|
7
|
+
path: 'docs/patram.md',
|
|
8
|
+
source: createMarkdownSource(),
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
expect(claims).toEqual(createExpectedMarkdownClaims());
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('uses the first line as a plain markdown title', () => {
|
|
15
|
+
const claims = parseClaims({
|
|
16
|
+
path: 'docs/plain-title.md',
|
|
17
|
+
source: ['Patram Plain Title', '', 'Body text.'].join('\n'),
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
expect(claims).toEqual([
|
|
21
|
+
{
|
|
22
|
+
document_id: 'doc:docs/plain-title.md',
|
|
23
|
+
id: 'claim:doc:docs/plain-title.md:1',
|
|
24
|
+
origin: {
|
|
25
|
+
column: 1,
|
|
26
|
+
line: 1,
|
|
27
|
+
path: 'docs/plain-title.md',
|
|
28
|
+
},
|
|
29
|
+
type: 'document.title',
|
|
30
|
+
value: 'Patram Plain Title',
|
|
31
|
+
},
|
|
32
|
+
]);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('returns no claims for unsupported file types', () => {
|
|
36
|
+
const claims = parseClaims({
|
|
37
|
+
path: 'bin/patram.js',
|
|
38
|
+
source: 'console.log("Patram");',
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
expect(claims).toEqual([]);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('returns no claims for extensionless files', () => {
|
|
45
|
+
const claims = parseClaims({
|
|
46
|
+
path: 'README',
|
|
47
|
+
source: '# Patram',
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
expect(claims).toEqual([]);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Create markdown input for parser tests.
|
|
55
|
+
*
|
|
56
|
+
* @returns {string}
|
|
57
|
+
*/
|
|
58
|
+
function createMarkdownSource() {
|
|
59
|
+
return [
|
|
60
|
+
'# Patram',
|
|
61
|
+
'',
|
|
62
|
+
'Read the [graph design](./graph-v0.md).',
|
|
63
|
+
'Defined by: terms/patram.md',
|
|
64
|
+
].join('\n');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Create the expected markdown claims.
|
|
69
|
+
*
|
|
70
|
+
* @returns {object[]}
|
|
71
|
+
*/
|
|
72
|
+
function createExpectedMarkdownClaims() {
|
|
73
|
+
return [
|
|
74
|
+
{
|
|
75
|
+
document_id: 'doc:docs/patram.md',
|
|
76
|
+
id: 'claim:doc:docs/patram.md:1',
|
|
77
|
+
origin: {
|
|
78
|
+
column: 1,
|
|
79
|
+
line: 1,
|
|
80
|
+
path: 'docs/patram.md',
|
|
81
|
+
},
|
|
82
|
+
type: 'document.title',
|
|
83
|
+
value: 'Patram',
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
document_id: 'doc:docs/patram.md',
|
|
87
|
+
id: 'claim:doc:docs/patram.md:2',
|
|
88
|
+
origin: {
|
|
89
|
+
column: 10,
|
|
90
|
+
line: 3,
|
|
91
|
+
path: 'docs/patram.md',
|
|
92
|
+
},
|
|
93
|
+
type: 'markdown.link',
|
|
94
|
+
value: {
|
|
95
|
+
target: './graph-v0.md',
|
|
96
|
+
text: 'graph design',
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
document_id: 'doc:docs/patram.md',
|
|
101
|
+
id: 'claim:doc:docs/patram.md:3',
|
|
102
|
+
name: 'defined_by',
|
|
103
|
+
origin: {
|
|
104
|
+
column: 1,
|
|
105
|
+
line: 4,
|
|
106
|
+
path: 'docs/patram.md',
|
|
107
|
+
},
|
|
108
|
+
parser: 'markdown',
|
|
109
|
+
type: 'directive',
|
|
110
|
+
value: 'terms/patram.md',
|
|
111
|
+
},
|
|
112
|
+
];
|
|
113
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export interface ParseClaimsInput {
|
|
2
|
+
path: string;
|
|
3
|
+
source: string;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export interface ClaimOrigin {
|
|
7
|
+
path: string;
|
|
8
|
+
line: number;
|
|
9
|
+
column: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface PatramClaim {
|
|
13
|
+
document_id: string;
|
|
14
|
+
id: string;
|
|
15
|
+
name?: string;
|
|
16
|
+
origin: ClaimOrigin;
|
|
17
|
+
parser?: string;
|
|
18
|
+
type: string;
|
|
19
|
+
value: string | { target: string; text: string };
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export type PatramClaimFields = Omit<
|
|
23
|
+
PatramClaim,
|
|
24
|
+
'document_id' | 'id' | 'origin' | 'type'
|
|
25
|
+
> & {
|
|
26
|
+
origin?: ClaimOrigin;
|
|
27
|
+
};
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @import { PatramConfig } from './patram-config.types.ts';
|
|
3
|
+
* @import { RefinementCtx } from 'zod';
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
|
|
8
|
+
const KIND_NAME_SCHEMA = z.string().min(1);
|
|
9
|
+
const RELATION_NAME_SCHEMA = z.string().min(1);
|
|
10
|
+
const CLAIM_TYPE_SCHEMA = z.string().min(1);
|
|
11
|
+
const TARGET_SCHEMA = z.enum(['path']);
|
|
12
|
+
|
|
13
|
+
const kind_definition_schema = z
|
|
14
|
+
.object({
|
|
15
|
+
builtin: z.boolean().optional(),
|
|
16
|
+
label: z.string().min(1).optional(),
|
|
17
|
+
})
|
|
18
|
+
.strict();
|
|
19
|
+
|
|
20
|
+
const relation_definition_schema = z
|
|
21
|
+
.object({
|
|
22
|
+
builtin: z.boolean().optional(),
|
|
23
|
+
from: z.array(KIND_NAME_SCHEMA).min(1),
|
|
24
|
+
to: z.array(KIND_NAME_SCHEMA).min(1),
|
|
25
|
+
})
|
|
26
|
+
.strict();
|
|
27
|
+
|
|
28
|
+
const mapping_node_schema = z
|
|
29
|
+
.object({
|
|
30
|
+
field: z.string().min(1),
|
|
31
|
+
kind: KIND_NAME_SCHEMA,
|
|
32
|
+
})
|
|
33
|
+
.strict();
|
|
34
|
+
|
|
35
|
+
const mapping_emit_schema = z
|
|
36
|
+
.object({
|
|
37
|
+
relation: RELATION_NAME_SCHEMA,
|
|
38
|
+
target: TARGET_SCHEMA,
|
|
39
|
+
target_kind: KIND_NAME_SCHEMA,
|
|
40
|
+
})
|
|
41
|
+
.strict();
|
|
42
|
+
|
|
43
|
+
const mapping_definition_schema = z
|
|
44
|
+
.object({
|
|
45
|
+
emit: mapping_emit_schema.optional(),
|
|
46
|
+
node: mapping_node_schema.optional(),
|
|
47
|
+
})
|
|
48
|
+
.strict()
|
|
49
|
+
.superRefine(validateMappingDefinition);
|
|
50
|
+
|
|
51
|
+
export const patramConfigSchema = z
|
|
52
|
+
.object({
|
|
53
|
+
$schema: z.url().optional(),
|
|
54
|
+
kinds: z.record(KIND_NAME_SCHEMA, kind_definition_schema),
|
|
55
|
+
mappings: z.record(CLAIM_TYPE_SCHEMA, mapping_definition_schema),
|
|
56
|
+
relations: z.record(RELATION_NAME_SCHEMA, relation_definition_schema),
|
|
57
|
+
})
|
|
58
|
+
.strict()
|
|
59
|
+
.superRefine(validatePatramConfigReferences);
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Parse and validate Patram JSON configuration.
|
|
63
|
+
*
|
|
64
|
+
* @param {unknown} config_json
|
|
65
|
+
* @returns {PatramConfig}
|
|
66
|
+
*/
|
|
67
|
+
export function parsePatramConfig(config_json) {
|
|
68
|
+
return patramConfigSchema.parse(config_json);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* @param {{ emit?: unknown, node?: unknown }} mapping_definition
|
|
73
|
+
* @param {RefinementCtx} refinement_context
|
|
74
|
+
*/
|
|
75
|
+
function validateMappingDefinition(mapping_definition, refinement_context) {
|
|
76
|
+
if (mapping_definition.emit || mapping_definition.node) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
refinement_context.addIssue({
|
|
81
|
+
code: 'custom',
|
|
82
|
+
message: 'Mapping must define at least one of "emit" or "node".',
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* @param {PatramConfig} config_json
|
|
88
|
+
* @param {RefinementCtx} refinement_context
|
|
89
|
+
*/
|
|
90
|
+
function validatePatramConfigReferences(config_json, refinement_context) {
|
|
91
|
+
validateRelationKinds(config_json, refinement_context);
|
|
92
|
+
validateMappingKinds(config_json, refinement_context);
|
|
93
|
+
validateMappingRelations(config_json, refinement_context);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* @param {PatramConfig} config_json
|
|
98
|
+
* @param {RefinementCtx} refinement_context
|
|
99
|
+
*/
|
|
100
|
+
function validateRelationKinds(config_json, refinement_context) {
|
|
101
|
+
for (const [relation_name, relation_definition] of Object.entries(
|
|
102
|
+
config_json.relations,
|
|
103
|
+
)) {
|
|
104
|
+
validateReferencedKinds(
|
|
105
|
+
relation_definition.from,
|
|
106
|
+
config_json.kinds,
|
|
107
|
+
['relations', relation_name, 'from'],
|
|
108
|
+
refinement_context,
|
|
109
|
+
);
|
|
110
|
+
validateReferencedKinds(
|
|
111
|
+
relation_definition.to,
|
|
112
|
+
config_json.kinds,
|
|
113
|
+
['relations', relation_name, 'to'],
|
|
114
|
+
refinement_context,
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* @param {PatramConfig} config_json
|
|
121
|
+
* @param {RefinementCtx} refinement_context
|
|
122
|
+
*/
|
|
123
|
+
function validateMappingKinds(config_json, refinement_context) {
|
|
124
|
+
for (const [mapping_name, mapping_definition] of Object.entries(
|
|
125
|
+
config_json.mappings,
|
|
126
|
+
)) {
|
|
127
|
+
if (mapping_definition.emit) {
|
|
128
|
+
validateReferencedKinds(
|
|
129
|
+
[mapping_definition.emit.target_kind],
|
|
130
|
+
config_json.kinds,
|
|
131
|
+
['mappings', mapping_name, 'emit', 'target_kind'],
|
|
132
|
+
refinement_context,
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (mapping_definition.node) {
|
|
137
|
+
validateReferencedKinds(
|
|
138
|
+
[mapping_definition.node.kind],
|
|
139
|
+
config_json.kinds,
|
|
140
|
+
['mappings', mapping_name, 'node', 'kind'],
|
|
141
|
+
refinement_context,
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* @param {PatramConfig} config_json
|
|
149
|
+
* @param {RefinementCtx} refinement_context
|
|
150
|
+
*/
|
|
151
|
+
function validateMappingRelations(config_json, refinement_context) {
|
|
152
|
+
for (const [mapping_name, mapping_definition] of Object.entries(
|
|
153
|
+
config_json.mappings,
|
|
154
|
+
)) {
|
|
155
|
+
if (!mapping_definition.emit) {
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (config_json.relations[mapping_definition.emit.relation]) {
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
refinement_context.addIssue({
|
|
164
|
+
code: 'custom',
|
|
165
|
+
message: `Unknown relation "${mapping_definition.emit.relation}".`,
|
|
166
|
+
path: ['mappings', mapping_name, 'emit', 'relation'],
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* @param {string[]} referenced_kinds
|
|
173
|
+
* @param {Record<string, unknown>} known_kinds
|
|
174
|
+
* @param {(string | number)[]} issue_path
|
|
175
|
+
* @param {RefinementCtx} refinement_context
|
|
176
|
+
*/
|
|
177
|
+
function validateReferencedKinds(
|
|
178
|
+
referenced_kinds,
|
|
179
|
+
known_kinds,
|
|
180
|
+
issue_path,
|
|
181
|
+
refinement_context,
|
|
182
|
+
) {
|
|
183
|
+
for (const referenced_kind of referenced_kinds) {
|
|
184
|
+
if (known_kinds[referenced_kind]) {
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
refinement_context.addIssue({
|
|
189
|
+
code: 'custom',
|
|
190
|
+
message: `Unknown kind "${referenced_kind}".`,
|
|
191
|
+
path: issue_path,
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { expect, it } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import graph_v0_config from '../docs/graph-v0.config.json' with { type: 'json' };
|
|
4
|
+
import { parsePatramConfig } from './patram-config.js';
|
|
5
|
+
|
|
6
|
+
it('parses the documented v0 config example', () => {
|
|
7
|
+
const config_json = parsePatramConfig(graph_v0_config);
|
|
8
|
+
|
|
9
|
+
expect(config_json.kinds.term.label).toBe('Term');
|
|
10
|
+
expect(config_json.mappings['markdown.directive.defined_by'].emit).toEqual({
|
|
11
|
+
relation: 'defines',
|
|
12
|
+
target: 'path',
|
|
13
|
+
target_kind: 'term',
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('rejects mappings that reference unknown relations', () => {
|
|
18
|
+
const issues = getIssues(createMissingRelationConfig());
|
|
19
|
+
|
|
20
|
+
expect(issues).toContainEqual(
|
|
21
|
+
expect.objectContaining({
|
|
22
|
+
message: 'Unknown relation "missing_relation".',
|
|
23
|
+
path: ['mappings', 'markdown.link', 'emit', 'relation'],
|
|
24
|
+
}),
|
|
25
|
+
);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('rejects mappings that reference unknown kinds', () => {
|
|
29
|
+
const issues = getIssues(createUnknownKindConfig());
|
|
30
|
+
|
|
31
|
+
expect(issues).toContainEqual(
|
|
32
|
+
expect.objectContaining({
|
|
33
|
+
message: 'Unknown kind "term".',
|
|
34
|
+
path: [
|
|
35
|
+
'mappings',
|
|
36
|
+
'markdown.directive.defined_by',
|
|
37
|
+
'emit',
|
|
38
|
+
'target_kind',
|
|
39
|
+
],
|
|
40
|
+
}),
|
|
41
|
+
);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('rejects mappings without semantic output', () => {
|
|
45
|
+
const issues = getIssues(createEmptyMappingConfig());
|
|
46
|
+
|
|
47
|
+
expect(issues).toContainEqual(
|
|
48
|
+
expect.objectContaining({
|
|
49
|
+
message: 'Mapping must define at least one of "emit" or "node".',
|
|
50
|
+
path: ['mappings', 'document.title'],
|
|
51
|
+
}),
|
|
52
|
+
);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Create a config with an unknown relation reference.
|
|
57
|
+
*
|
|
58
|
+
* @returns {object}
|
|
59
|
+
*/
|
|
60
|
+
function createMissingRelationConfig() {
|
|
61
|
+
return {
|
|
62
|
+
kinds: {
|
|
63
|
+
document: {
|
|
64
|
+
builtin: true,
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
mappings: {
|
|
68
|
+
'markdown.link': {
|
|
69
|
+
emit: {
|
|
70
|
+
relation: 'missing_relation',
|
|
71
|
+
target: 'path',
|
|
72
|
+
target_kind: 'document',
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
relations: {},
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Create a config with an unknown kind reference.
|
|
82
|
+
*
|
|
83
|
+
* @returns {object}
|
|
84
|
+
*/
|
|
85
|
+
function createUnknownKindConfig() {
|
|
86
|
+
return {
|
|
87
|
+
kinds: {
|
|
88
|
+
document: {
|
|
89
|
+
builtin: true,
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
mappings: {
|
|
93
|
+
'markdown.directive.defined_by': {
|
|
94
|
+
emit: {
|
|
95
|
+
relation: 'links_to',
|
|
96
|
+
target: 'path',
|
|
97
|
+
target_kind: 'term',
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
relations: {
|
|
102
|
+
links_to: {
|
|
103
|
+
from: ['document'],
|
|
104
|
+
to: ['document'],
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Create a config with an empty mapping definition.
|
|
112
|
+
*
|
|
113
|
+
* @returns {object}
|
|
114
|
+
*/
|
|
115
|
+
function createEmptyMappingConfig() {
|
|
116
|
+
return {
|
|
117
|
+
kinds: {
|
|
118
|
+
document: {
|
|
119
|
+
builtin: true,
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
mappings: {
|
|
123
|
+
'document.title': {},
|
|
124
|
+
},
|
|
125
|
+
relations: {},
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Parse config and return validation issues.
|
|
131
|
+
*
|
|
132
|
+
* @param {object} config_json
|
|
133
|
+
* @returns {unknown[]}
|
|
134
|
+
*/
|
|
135
|
+
function getIssues(config_json) {
|
|
136
|
+
try {
|
|
137
|
+
parsePatramConfig(config_json);
|
|
138
|
+
} catch (error) {
|
|
139
|
+
if (error instanceof Error && 'issues' in error) {
|
|
140
|
+
return /** @type {{ issues: unknown[] }} */ (error).issues;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
throw error;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
throw new Error('Expected configuration parsing to fail.');
|
|
147
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export interface KindDefinition {
|
|
2
|
+
builtin?: boolean;
|
|
3
|
+
label?: string;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export interface RelationDefinition {
|
|
7
|
+
builtin?: boolean;
|
|
8
|
+
from: string[];
|
|
9
|
+
to: string[];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface MappingNodeDefinition {
|
|
13
|
+
field: string;
|
|
14
|
+
kind: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface MappingEmitDefinition {
|
|
18
|
+
relation: string;
|
|
19
|
+
target: 'path';
|
|
20
|
+
target_kind: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface MappingDefinition {
|
|
24
|
+
emit?: MappingEmitDefinition;
|
|
25
|
+
node?: MappingNodeDefinition;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface PatramConfig {
|
|
29
|
+
$schema?: string;
|
|
30
|
+
kinds: Record<string, KindDefinition>;
|
|
31
|
+
mappings: Record<string, MappingDefinition>;
|
|
32
|
+
relations: Record<string, RelationDefinition>;
|
|
33
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "patram",
|
|
3
|
+
"version": "0.0.2",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"files": [
|
|
6
|
+
"bin/",
|
|
7
|
+
"lib/"
|
|
8
|
+
],
|
|
9
|
+
"bin": {
|
|
10
|
+
"patram": "./bin/patram.js"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://github.com/mantoni/patram",
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "git+https://github.com/mantoni/patram.git"
|
|
16
|
+
},
|
|
17
|
+
"engines": {
|
|
18
|
+
"node": ">=22"
|
|
19
|
+
},
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"scripts": {
|
|
22
|
+
"all": "npm run check:lint && npm run check:format && npm run check:types && npm run test:unit && npm run test:coverage && npm run check:dupes",
|
|
23
|
+
"check:dupes": "jscpd --min-tokens 100 --min-lines 6 --mode mild --threshold 0 --reporters console --gitignore .",
|
|
24
|
+
"check:format": "prettier --check .",
|
|
25
|
+
"check:lint": "eslint .",
|
|
26
|
+
"check:staged": "lint-staged --quiet",
|
|
27
|
+
"check:types": "tsc",
|
|
28
|
+
"postversion": "git push && git push --tags",
|
|
29
|
+
"preversion": "npm run all",
|
|
30
|
+
"prepare": "husky",
|
|
31
|
+
"test": "npm run test:unit && npm run test:coverage",
|
|
32
|
+
"test:coverage": "vitest run --coverage",
|
|
33
|
+
"test:unit": "vitest run",
|
|
34
|
+
"version": "node scripts/update-changelog.js && git add CHANGELOG.md package.json package-lock.json"
|
|
35
|
+
},
|
|
36
|
+
"lint-staged": {
|
|
37
|
+
"*.{js,ts,json,md}": "prettier --check",
|
|
38
|
+
"*.{js,ts}": [
|
|
39
|
+
"eslint",
|
|
40
|
+
"vitest related --run --passWithNoTests"
|
|
41
|
+
]
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"zod": "^4.3.6"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@eslint/js": "^10.0.1",
|
|
48
|
+
"@types/node": "^24.12.0",
|
|
49
|
+
"@vitest/coverage-v8": "^4.1.0",
|
|
50
|
+
"ansis": "^4.2.0",
|
|
51
|
+
"eslint": "^10.0.3",
|
|
52
|
+
"eslint-plugin-jsdoc": "^62.8.0",
|
|
53
|
+
"globals": "^17.4.0",
|
|
54
|
+
"husky": "^9.1.7",
|
|
55
|
+
"jscpd": "^4.0.8",
|
|
56
|
+
"lint-staged": "^16.2.6",
|
|
57
|
+
"prettier": "^3.5.3",
|
|
58
|
+
"slice-ansi": "^8.0.0",
|
|
59
|
+
"string-width": "^8.2.0",
|
|
60
|
+
"typescript": "^5.8.2",
|
|
61
|
+
"vitest": "^4.1.0",
|
|
62
|
+
"wrap-ansi": "^10.0.0"
|
|
63
|
+
}
|
|
64
|
+
}
|