@concordance-wiki/typing 0.1.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.
package/README.md ADDED
@@ -0,0 +1,7 @@
1
+ # @concordance-wiki/typing
2
+
3
+ Type cascade, identifiers, application and domain resolution.
4
+
5
+ Today: `resolveType` runs the type cascade of one markdown file, in increasing precedence the source's `default_type`, the source's `type`, the typing rules in order (`path` glob, `suffix`, `ext`, `frontmatter` criteria, every criterion of a rule required, the last match wins) and the frontmatter `type`; it records the origin (`source`, `rule#3`, `suffix`, `frontmatter`), the attribute defaults set by the matching rules, `E-TYPE-CONFLICT` when the frontmatter contradicts a suffix rule (the frontmatter is kept) and `W-TYPE-UNKNOWN` when the profile does not declare the type (the note becomes a `document`). `buildEntity` turns one ingested and parsed file into an `Entity`: deterministic identifier, title (frontmatter, H1, file name), aliases, status, summary (frontmatter, first paragraph), the frontmatter attributes over the rule defaults with `W-ATTRIBUTE-UNKNOWN` for the keys the type and the common attributes do not declare, the source location, the type origin and `graph` (`documents-only` for the types the profile marks so, `document` and `meeting` by default). `compileDomains` flattens the `domains` tree of the configuration into id paths (`inference/recognition`) with one glob matcher each, and `resolveDomain` files one path under the frontmatter `domain` (by id or id path; an unknown value is kept as written and marked undeclared), else under the deepest domain whose globs match the source-relative path (the last declared wins at equal depth), else under `unclassified`. `resolveApplication` takes the frontmatter `application`, else a rule's `set.application`, else the source's `application`, and says whether `applications:` declares it. `buildEntity` records both on the entity and `filingFindings` reports `W-APP-MISSING`, `W-APP-UNKNOWN`, `W-DOMAIN-UNCLASSIFIED` and `W-DOMAIN-UNKNOWN`, the first and the third never for the container types (applications and domains as notes). `typeSources` does it for every parsed markdown file of the ingested sources, resolves duplicate identifiers (`E-ID-DUP`) and returns entities and findings in canonical order.
6
+
7
+ Part of [Concordance](../../README.md).
@@ -0,0 +1,19 @@
1
+ import type { ApplicationConfig, SourceConfig } from "@concordance-wiki/core";
2
+ export interface ResolveApplicationInput {
3
+ source: SourceConfig;
4
+ /** Attribute values set by the matching typing rules; `application` is the one read here. */
5
+ ruleDefaults: Record<string, unknown>;
6
+ frontmatterApplication: unknown;
7
+ applications: readonly ApplicationConfig[];
8
+ }
9
+ export type ApplicationOrigin = "frontmatter" | "rule" | "source" | "none";
10
+ export interface ResolvedApplication {
11
+ /** Absent when nothing sets it: the entity then yields `W-APP-MISSING`. */
12
+ application?: string;
13
+ origin: ApplicationOrigin;
14
+ /** True when `applications:` declares the resolved identifier; false when nothing sets it. */
15
+ declared: boolean;
16
+ }
17
+ /** Decreasing precedence: the frontmatter `application`, a rule's `set.application`, the source's `application`. */
18
+ export declare function resolveApplication(input: ResolveApplicationInput): ResolvedApplication;
19
+ //# sourceMappingURL=application.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"application.d.ts","sourceRoot":"","sources":["../src/application.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAI9E,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,YAAY,CAAC;IACrB,6FAA6F;IAC7F,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,sBAAsB,EAAE,OAAO,CAAC;IAChC,YAAY,EAAE,SAAS,iBAAiB,EAAE,CAAC;CAC5C;AAED,MAAM,MAAM,iBAAiB,GAAG,aAAa,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,CAAC;AAE3E,MAAM,WAAW,mBAAmB;IAClC,2EAA2E;IAC3E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,iBAAiB,CAAC;IAC1B,8FAA8F;IAC9F,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,oHAAoH;AACpH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,uBAAuB,GAAG,mBAAmB,CAatF"}
@@ -0,0 +1,18 @@
1
+ import { declaredString } from "./frontmatter.js";
2
+ /** Decreasing precedence: the frontmatter `application`, a rule's `set.application`, the source's `application`. */
3
+ export function resolveApplication(input) {
4
+ const { source, ruleDefaults, frontmatterApplication, applications } = input;
5
+ const steps = [
6
+ [declaredString(frontmatterApplication), "frontmatter"],
7
+ [declaredString(ruleDefaults["application"]), "rule"],
8
+ [source.application, "source"],
9
+ ];
10
+ for (const [application, origin] of steps) {
11
+ if (application === undefined)
12
+ continue;
13
+ const declared = applications.some((candidate) => candidate.id === application);
14
+ return { application, origin, declared };
15
+ }
16
+ return { origin: "none", declared: false };
17
+ }
18
+ //# sourceMappingURL=application.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"application.js","sourceRoot":"","sources":["../src/application.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAoBlD,oHAAoH;AACpH,MAAM,UAAU,kBAAkB,CAAC,KAA8B;IAC/D,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,sBAAsB,EAAE,YAAY,EAAE,GAAG,KAAK,CAAC;IAC7E,MAAM,KAAK,GAA8C;QACvD,CAAC,cAAc,CAAC,sBAAsB,CAAC,EAAE,aAAa,CAAC;QACvD,CAAC,cAAc,CAAC,YAAY,CAAC,aAAa,CAAC,CAAC,EAAE,MAAM,CAAC;QACrD,CAAC,MAAM,CAAC,WAAW,EAAE,QAAQ,CAAC;KAC/B,CAAC;IACF,KAAK,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QAC1C,IAAI,WAAW,KAAK,SAAS;YAAE,SAAS;QACxC,MAAM,QAAQ,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,EAAE,KAAK,WAAW,CAAC,CAAC;QAChF,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;IAC3C,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;AAC7C,CAAC"}
@@ -0,0 +1,26 @@
1
+ import { type Finding, type SourceConfig, type TypeOrigin, type TypingRuleMatch } from "@concordance-wiki/core";
2
+ import type { Profile } from "@concordance-wiki/profile";
3
+ /** The type every markdown file gets when neither the source nor a rule nor the frontmatter says otherwise. */
4
+ export declare const FALLBACK_TYPE = "document";
5
+ export interface ResolveTypeInput {
6
+ source: SourceConfig;
7
+ /** Forward-slash path relative to the source root. */
8
+ path: string;
9
+ frontmatter: Record<string, unknown>;
10
+ profile: Profile;
11
+ }
12
+ export interface ResolvedType {
13
+ type: string;
14
+ origin: TypeOrigin;
15
+ /** Attribute values set by the matching rules, `type` excluded; the frontmatter overrides them. */
16
+ defaults: Record<string, unknown>;
17
+ findings: Finding[];
18
+ }
19
+ /** Every criterion of a rule must hold; a rule always has at least one, the configuration schema says so. */
20
+ export declare function ruleMatches(match: TypingRuleMatch, path: string, frontmatter: Record<string, unknown>): boolean;
21
+ /**
22
+ * Increasing precedence: the source's `default_type`, the source's `type`, the rules in order
23
+ * (the last match wins), then the frontmatter.
24
+ */
25
+ export declare function resolveType(input: ResolveTypeInput): ResolvedType;
26
+ //# sourceMappingURL=cascade.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cascade.d.ts","sourceRoot":"","sources":["../src/cascade.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,OAAO,EACZ,KAAK,YAAY,EACjB,KAAK,UAAU,EAEf,KAAK,eAAe,EACrB,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAC;AAIzD,+GAA+G;AAC/G,eAAO,MAAM,aAAa,aAAa,CAAC;AAExC,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,YAAY,CAAC;IACrB,sDAAsD;IACtD,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,UAAU,CAAC;IACnB,mGAAmG;IACnG,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,QAAQ,EAAE,OAAO,EAAE,CAAC;CACrB;AAOD,6GAA6G;AAC7G,wBAAgB,WAAW,CACzB,KAAK,EAAE,eAAe,EACtB,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACnC,OAAO,CAKT;AAgCD;;;GAGG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,GAAG,YAAY,CA8BjE"}
@@ -0,0 +1,78 @@
1
+ import { compileGlobs, } from "@concordance-wiki/core";
2
+ import { declaredString } from "./frontmatter.js";
3
+ /** The type every markdown file gets when neither the source nor a rule nor the frontmatter says otherwise. */
4
+ export const FALLBACK_TYPE = "document";
5
+ /** Every criterion of a rule must hold; a rule always has at least one, the configuration schema says so. */
6
+ export function ruleMatches(match, path, frontmatter) {
7
+ if (match.path !== undefined && !compileGlobs([match.path])(path))
8
+ return false;
9
+ if (match.suffix !== undefined && !path.endsWith(match.suffix))
10
+ return false;
11
+ if (match.ext !== undefined && !match.ext.some((ext) => path.endsWith(ext)))
12
+ return false;
13
+ return match.frontmatter === undefined || Object.hasOwn(frontmatter, match.frontmatter);
14
+ }
15
+ function ruleOrigin(rule, index) {
16
+ if (rule.match.suffix !== undefined)
17
+ return "suffix";
18
+ // A 1-based decimal position always satisfies the `rule#<digits>` member of TypeOrigin.
19
+ return `rule#${String(index + 1)}`;
20
+ }
21
+ function typeConflict(input, bySuffix, byFrontmatter) {
22
+ return {
23
+ check: "E-TYPE-CONFLICT",
24
+ severity: "error",
25
+ source: input.source.name,
26
+ path: input.path,
27
+ message: `frontmatter type "${byFrontmatter}" of ${input.path} contradicts the type "${bySuffix}" given by the file suffix; the frontmatter is kept`,
28
+ remediation: "Align the frontmatter type with the suffix, or drop the type key and let the filing convention decide.",
29
+ };
30
+ }
31
+ function typeUnknown(input, type, origin) {
32
+ return {
33
+ check: "W-TYPE-UNKNOWN",
34
+ severity: "warning",
35
+ source: input.source.name,
36
+ path: input.path,
37
+ message: `type "${type}" of ${input.path} (from ${origin}) is not declared by the profile; the note is treated as a ${FALLBACK_TYPE}`,
38
+ remediation: "Use a type of the profile, declare the type in the project profile, or fix the source rule or the frontmatter that sets it.",
39
+ };
40
+ }
41
+ /**
42
+ * Increasing precedence: the source's `default_type`, the source's `type`, the rules in order
43
+ * (the last match wins), then the frontmatter.
44
+ */
45
+ export function resolveType(input) {
46
+ const { source, path, frontmatter, profile } = input;
47
+ const findings = [];
48
+ const defaults = {};
49
+ let step = { type: source.default_type ?? FALLBACK_TYPE, origin: "source" };
50
+ if (source.type !== undefined) {
51
+ step = { type: source.type, origin: "source" };
52
+ }
53
+ (source.rules ?? []).forEach((rule, index) => {
54
+ if (!ruleMatches(rule.match, path, frontmatter))
55
+ return;
56
+ for (const [key, value] of Object.entries(rule.set)) {
57
+ if (key === "type") {
58
+ step = { type: String(value), origin: ruleOrigin(rule, index) };
59
+ }
60
+ else {
61
+ defaults[key] = value;
62
+ }
63
+ }
64
+ });
65
+ const type = declaredString(frontmatter["type"]);
66
+ if (type !== undefined) {
67
+ if (step.origin === "suffix" && type !== step.type) {
68
+ findings.push(typeConflict(input, step.type, type));
69
+ }
70
+ step = { type, origin: "frontmatter" };
71
+ }
72
+ if (!Object.hasOwn(profile.types, step.type)) {
73
+ findings.push(typeUnknown(input, step.type, step.origin));
74
+ step = { type: FALLBACK_TYPE, origin: step.origin };
75
+ }
76
+ return { type: step.type, origin: step.origin, defaults, findings };
77
+ }
78
+ //# sourceMappingURL=cascade.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cascade.js","sourceRoot":"","sources":["../src/cascade.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ import { type DomainConfig, type PathMatcher } from "@concordance-wiki/core";
2
+ /** The domain of every note that no declared domain claims. */
3
+ export declare const UNCLASSIFIED_DOMAIN = "unclassified";
4
+ export interface CompiledDomain {
5
+ id: string;
6
+ /** The identifiers from the root domain down to this one, `inference/recognition` style. */
7
+ path: string;
8
+ /** Zero for a root domain. */
9
+ depth: number;
10
+ matcher: PathMatcher;
11
+ /** True when the domain declares at least one glob. */
12
+ globs: boolean;
13
+ /** The name of the folder that claims files, absent for a domain declared by globs or frontmatter alone. */
14
+ folder?: string;
15
+ parent?: CompiledDomain;
16
+ }
17
+ /** The domain tree flattened in declaration order, each parent before its subdomains. */
18
+ export type DomainMatcher = readonly CompiledDomain[];
19
+ export type DomainOrigin = "frontmatter" | "folder" | "glob" | "unclassified";
20
+ export interface ResolvedDomain {
21
+ /** The full identifier path of the domain, or `unclassified`. */
22
+ domain: string;
23
+ origin: DomainOrigin;
24
+ /** False only for a frontmatter domain that no declared domain answers to. */
25
+ declared: boolean;
26
+ }
27
+ export declare function compileDomains(domains: readonly DomainConfig[]): DomainMatcher;
28
+ /**
29
+ * Decreasing precedence: the frontmatter `domain`, the folders and globs evaluated on the
30
+ * source-relative path, then `unclassified`.
31
+ */
32
+ export declare function resolveDomain(relativePath: string, frontmatterDomain: unknown, domains: DomainMatcher): ResolvedDomain;
33
+ //# sourceMappingURL=domains.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"domains.d.ts","sourceRoot":"","sources":["../src/domains.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,YAAY,EAAE,KAAK,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAI3F,+DAA+D;AAC/D,eAAO,MAAM,mBAAmB,iBAAiB,CAAC;AAElD,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,4FAA4F;IAC5F,IAAI,EAAE,MAAM,CAAC;IACb,8BAA8B;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,WAAW,CAAC;IACrB,uDAAuD;IACvD,KAAK,EAAE,OAAO,CAAC;IACf,4GAA4G;IAC5G,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,cAAc,CAAC;CACzB;AAED,yFAAyF;AACzF,MAAM,MAAM,aAAa,GAAG,SAAS,cAAc,EAAE,CAAC;AAEtD,MAAM,MAAM,YAAY,GAAG,aAAa,GAAG,QAAQ,GAAG,MAAM,GAAG,cAAc,CAAC;AAE9E,MAAM,WAAW,cAAc;IAC7B,iEAAiE;IACjE,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,YAAY,CAAC;IACrB,8EAA8E;IAC9E,QAAQ,EAAE,OAAO,CAAC;CACnB;AA8BD,wBAAgB,cAAc,CAAC,OAAO,EAAE,SAAS,YAAY,EAAE,GAAG,aAAa,CAE9E;AAgED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,YAAY,EAAE,MAAM,EACpB,iBAAiB,EAAE,OAAO,EAC1B,OAAO,EAAE,aAAa,GACrB,cAAc,CAYhB"}
@@ -0,0 +1,96 @@
1
+ import { compileGlobs } from "@concordance-wiki/core";
2
+ import { declaredString } from "./frontmatter.js";
3
+ /** The domain of every note that no declared domain claims. */
4
+ export const UNCLASSIFIED_DOMAIN = "unclassified";
5
+ function folderOf(domain) {
6
+ if (domain.folder === true)
7
+ return domain.id;
8
+ if (typeof domain.folder === "string")
9
+ return domain.folder;
10
+ return undefined;
11
+ }
12
+ function flatten(domains, parent, depth) {
13
+ return domains.flatMap((domain) => {
14
+ const path = parent === undefined ? domain.id : `${parent.path}/${domain.id}`;
15
+ const globs = domain.match ?? [];
16
+ const folder = folderOf(domain);
17
+ const compiled = {
18
+ id: domain.id,
19
+ path,
20
+ depth,
21
+ matcher: compileGlobs(globs),
22
+ globs: globs.length > 0,
23
+ ...(folder === undefined ? {} : { folder }),
24
+ ...(parent === undefined ? {} : { parent }),
25
+ };
26
+ return [compiled, ...flatten(domain.subdomains ?? [], compiled, depth + 1)];
27
+ });
28
+ }
29
+ export function compileDomains(domains) {
30
+ return flatten(domains, undefined, 0);
31
+ }
32
+ /** A frontmatter value names a domain by its full path first, else by the identifier of the first domain declared with it. */
33
+ function declaredDomain(value, domains) {
34
+ return (domains.find((domain) => domain.path === value) ?? domains.find((domain) => domain.id === value));
35
+ }
36
+ /** The directory segments of a source-relative path, the file name excluded. */
37
+ function directoriesOf(relativePath) {
38
+ return relativePath.split("/").slice(0, -1);
39
+ }
40
+ /**
41
+ * The positions, among the directory segments, where the folder of the domain sits; none for
42
+ * a domain without folder. A subdomain's folder counts only under its parent's folder; when
43
+ * the parent is declared by globs instead, it counts anywhere on a path the parent's globs
44
+ * match; a parent declared by neither constrains nothing.
45
+ */
46
+ function folderPositions(domain, directories, relativePath) {
47
+ const own = directories.flatMap((segment, index) => (segment === domain.folder ? [index] : []));
48
+ const { parent } = domain;
49
+ if (parent === undefined)
50
+ return own;
51
+ if (parent.folder !== undefined) {
52
+ const first = Math.min(...folderPositions(parent, directories, relativePath));
53
+ return own.filter((index) => index > first);
54
+ }
55
+ return !parent.globs || parent.matcher(relativePath) ? own : [];
56
+ }
57
+ /** How a domain claims a file, its folder before its globs; undefined when it does not. */
58
+ function claimOf(domain, relativePath, directories) {
59
+ if (folderPositions(domain, directories, relativePath).length > 0)
60
+ return { domain, origin: "folder" };
61
+ if (domain.matcher(relativePath))
62
+ return { domain, origin: "glob" };
63
+ return undefined;
64
+ }
65
+ /** Among the domains that claim the file, the deepest wins; between domains of the same depth, the last declared. */
66
+ function matchingDomain(relativePath, domains) {
67
+ const directories = directoriesOf(relativePath);
68
+ let best;
69
+ for (const domain of domains) {
70
+ const claim = claimOf(domain, relativePath, directories);
71
+ if (claim === undefined)
72
+ continue;
73
+ if (best === undefined || claim.domain.depth >= best.domain.depth)
74
+ best = claim;
75
+ }
76
+ return best;
77
+ }
78
+ /**
79
+ * Decreasing precedence: the frontmatter `domain`, the folders and globs evaluated on the
80
+ * source-relative path, then `unclassified`.
81
+ */
82
+ export function resolveDomain(relativePath, frontmatterDomain, domains) {
83
+ const declared = declaredString(frontmatterDomain);
84
+ if (declared !== undefined) {
85
+ const found = declaredDomain(declared, domains);
86
+ if (found !== undefined)
87
+ return { domain: found.path, origin: "frontmatter", declared: true };
88
+ return { domain: declared, origin: "frontmatter", declared: declared === UNCLASSIFIED_DOMAIN };
89
+ }
90
+ const matched = matchingDomain(relativePath, domains);
91
+ if (matched !== undefined) {
92
+ return { domain: matched.domain.path, origin: matched.origin, declared: true };
93
+ }
94
+ return { domain: UNCLASSIFIED_DOMAIN, origin: "unclassified", declared: true };
95
+ }
96
+ //# sourceMappingURL=domains.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"domains.js","sourceRoot":"","sources":["../src/domains.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import { type ApplicationConfig, type Entity, type Finding, type SourceConfig } from "@concordance-wiki/core";
2
+ import type { IngestedFile, IngestedSource, ParsedMarkdown } from "@concordance-wiki/ingest";
3
+ import type { Profile } from "@concordance-wiki/profile";
4
+ import { type DomainMatcher } from "./domains.js";
5
+ export interface BuildEntityInput {
6
+ file: IngestedFile;
7
+ source: IngestedSource;
8
+ sourceConfig: SourceConfig;
9
+ document: ParsedMarkdown;
10
+ profile: Profile;
11
+ applications: readonly ApplicationConfig[];
12
+ domains: DomainMatcher;
13
+ }
14
+ export interface BuiltEntity {
15
+ entity: Entity;
16
+ findings: Finding[];
17
+ }
18
+ export declare function typeSuffixesOf(source: SourceConfig): string[];
19
+ export declare function buildEntity(input: BuildEntityInput): BuiltEntity;
20
+ //# sourceMappingURL=entity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entity.d.ts","sourceRoot":"","sources":["../src/entity.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,iBAAiB,EACtB,KAAK,MAAM,EAEX,KAAK,OAAO,EACZ,KAAK,YAAY,EAClB,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC7F,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAC;AAIzD,OAAO,EAAiB,KAAK,aAAa,EAAE,MAAM,cAAc,CAAC;AAGjE,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,YAAY,CAAC;IACnB,MAAM,EAAE,cAAc,CAAC;IACvB,YAAY,EAAE,YAAY,CAAC;IAC3B,QAAQ,EAAE,cAAc,CAAC;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,SAAS,iBAAiB,EAAE,CAAC;IAC3C,OAAO,EAAE,aAAa,CAAC;CACxB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,EAAE,CAAC;CACrB;AAiBD,wBAAgB,cAAc,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,EAAE,CAI7D;AA6FD,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,GAAG,WAAW,CAgDhE"}
package/dist/entity.js ADDED
@@ -0,0 +1,149 @@
1
+ import { identifierFor, } from "@concordance-wiki/core";
2
+ import { resolveApplication } from "./application.js";
3
+ import { resolveType } from "./cascade.js";
4
+ import { resolveDomain } from "./domains.js";
5
+ import { filingFindings } from "./filing.js";
6
+ /** Frontmatter keys that become fields of the entity rather than attributes. */
7
+ const COMMON_KEYS = new Set([
8
+ "id",
9
+ "type",
10
+ "title",
11
+ "aliases",
12
+ "status",
13
+ "summary",
14
+ "tags",
15
+ "application",
16
+ "domain",
17
+ ]);
18
+ const DEFAULT_STATUS = "valid";
19
+ export function typeSuffixesOf(source) {
20
+ return (source.rules ?? []).flatMap((rule) => rule.match.suffix === undefined ? [] : [rule.match.suffix]);
21
+ }
22
+ // Code-unit order, not locale order: the output must not depend on the collation data of the runtime.
23
+ function byCodeUnit(a, b) {
24
+ return Number(a > b) - Number(a < b);
25
+ }
26
+ function fileTitle(path) {
27
+ const name = path.slice(path.lastIndexOf("/") + 1);
28
+ return name.replace(/\.[^.]+$/, "");
29
+ }
30
+ function titleOf(frontmatter, document, path) {
31
+ if (typeof frontmatter["title"] === "string")
32
+ return frontmatter["title"];
33
+ return document.title ?? fileTitle(path);
34
+ }
35
+ function aliasesOf(frontmatter) {
36
+ const { aliases } = frontmatter;
37
+ if (!Array.isArray(aliases))
38
+ return [];
39
+ return aliases.filter((alias) => typeof alias === "string");
40
+ }
41
+ function statusOf(frontmatter, profile) {
42
+ if (typeof frontmatter["status"] === "string")
43
+ return frontmatter["status"];
44
+ const fallback = profile.common_attributes?.["status"]?.default;
45
+ return typeof fallback === "string" ? fallback : DEFAULT_STATUS;
46
+ }
47
+ function summaryOf(frontmatter, document) {
48
+ if (typeof frontmatter["summary"] === "string")
49
+ return frontmatter["summary"];
50
+ return document.paragraphs[0]?.text;
51
+ }
52
+ function attributesOf(frontmatter, defaults) {
53
+ const merged = {};
54
+ // A rule's application feeds the application cascade, not the attributes.
55
+ for (const [key, value] of Object.entries(defaults)) {
56
+ if (key !== "application")
57
+ merged[key] = value;
58
+ }
59
+ for (const [key, value] of Object.entries(frontmatter)) {
60
+ if (!COMMON_KEYS.has(key))
61
+ merged[key] = value;
62
+ }
63
+ const attributes = {};
64
+ for (const key of Object.keys(merged).sort(byCodeUnit)) {
65
+ attributes[key] = merged[key];
66
+ }
67
+ return attributes;
68
+ }
69
+ function sourceOf(file, source) {
70
+ // A note is the whole file: its first line locates it.
71
+ const location = {
72
+ name: source.name,
73
+ path: file.path,
74
+ line: 1,
75
+ last_modified: file.modifiedAt,
76
+ };
77
+ return file.commit === undefined ? location : { ...location, commit: file.commit };
78
+ }
79
+ function unknownAttributes(input, type, id) {
80
+ const { profile } = input;
81
+ const known = new Set([
82
+ "id",
83
+ "type",
84
+ ...Object.keys(profile.common_attributes ?? {}),
85
+ ...Object.keys(profile.types[type]?.attributes ?? {}),
86
+ ]);
87
+ return Object.keys(input.document.frontmatter)
88
+ .filter((key) => !known.has(key))
89
+ .map((key) => ({
90
+ check: "W-ATTRIBUTE-UNKNOWN",
91
+ severity: "warning",
92
+ source: input.source.name,
93
+ path: input.file.path,
94
+ entity: id,
95
+ message: `frontmatter attribute "${key}" of ${input.file.path} is not declared for type ${type}; it is kept as-is`,
96
+ remediation: "Use an attribute of the type, declare it in the project profile, or remove the key.",
97
+ }));
98
+ }
99
+ export function buildEntity(input) {
100
+ const { file, source, sourceConfig, document, profile, applications, domains } = input;
101
+ const { frontmatter } = document;
102
+ const identifier = identifierFor({
103
+ source: source.name,
104
+ path: file.path,
105
+ typeSuffixes: typeSuffixesOf(sourceConfig),
106
+ frontmatterId: frontmatter["id"],
107
+ });
108
+ const resolved = resolveType({ source: sourceConfig, path: file.path, frontmatter, profile });
109
+ const application = resolveApplication({
110
+ source: sourceConfig,
111
+ ruleDefaults: resolved.defaults,
112
+ frontmatterApplication: frontmatter["application"],
113
+ applications,
114
+ });
115
+ const domain = resolveDomain(file.path, frontmatter["domain"], domains);
116
+ const entity = {
117
+ id: identifier.id,
118
+ type: resolved.type,
119
+ title: titleOf(frontmatter, document, file.path),
120
+ aliases: aliasesOf(frontmatter),
121
+ locale: source.locale,
122
+ ...(application.application === undefined ? {} : { application: application.application }),
123
+ domain: domain.domain,
124
+ status: statusOf(frontmatter, profile),
125
+ type_origin: resolved.origin,
126
+ graph: profile.types[resolved.type]?.graph ?? "full",
127
+ attributes: attributesOf(frontmatter, resolved.defaults),
128
+ source: sourceOf(file, source),
129
+ };
130
+ const summary = summaryOf(frontmatter, document);
131
+ if (summary !== undefined)
132
+ entity.summary = summary;
133
+ const findings = [
134
+ ...(identifier.finding === undefined ? [] : [identifier.finding]),
135
+ ...resolved.findings,
136
+ ...unknownAttributes(input, resolved.type, identifier.id),
137
+ ...filingFindings({
138
+ id: identifier.id,
139
+ type: resolved.type,
140
+ source: source.name,
141
+ path: file.path,
142
+ profile,
143
+ application,
144
+ domain,
145
+ }),
146
+ ];
147
+ return { entity, findings };
148
+ }
149
+ //# sourceMappingURL=entity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entity.js","sourceRoot":"","sources":["../src/entity.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import type { Finding } from "@concordance-wiki/core";
2
+ import type { Profile } from "@concordance-wiki/profile";
3
+ import type { ResolvedApplication } from "./application.js";
4
+ import { type ResolvedDomain } from "./domains.js";
5
+ export interface FilingInput {
6
+ id: string;
7
+ type: string;
8
+ source: string;
9
+ /** Forward-slash path relative to the source root. */
10
+ path: string;
11
+ profile: Profile;
12
+ application: ResolvedApplication;
13
+ domain: ResolvedDomain;
14
+ }
15
+ /** The filing findings of one entity; containers are exempt from the missing-application and unclassified ones. */
16
+ export declare function filingFindings(input: FilingInput): Finding[];
17
+ //# sourceMappingURL=filing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filing.d.ts","sourceRoot":"","sources":["../src/filing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAC;AAEzD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,EAAuB,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAKxE,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,sDAAsD;IACtD,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,mBAAmB,CAAC;IACjC,MAAM,EAAE,cAAc,CAAC;CACxB;AAuED,mHAAmH;AACnH,wBAAgB,cAAc,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,EAAE,CAG5D"}
package/dist/filing.js ADDED
@@ -0,0 +1,48 @@
1
+ import { UNCLASSIFIED_DOMAIN } from "./domains.js";
2
+ /** Applications and domains are what entities are filed into; they are not filed themselves. */
3
+ const CONTAINER_GROUP = "container";
4
+ function finding(input, check, severity, message, remediation) {
5
+ return {
6
+ check,
7
+ severity,
8
+ source: input.source,
9
+ path: input.path,
10
+ entity: input.id,
11
+ message,
12
+ remediation,
13
+ };
14
+ }
15
+ function applicationFindings(input, filed) {
16
+ const { application } = input;
17
+ if (application.application === undefined) {
18
+ if (!filed)
19
+ return [];
20
+ return [
21
+ finding(input, "W-APP-MISSING", "warning", `${input.id} resolves to no application`, "Set application on the source, in a typing rule, or in the note's frontmatter."),
22
+ ];
23
+ }
24
+ if (application.declared)
25
+ return [];
26
+ return [
27
+ finding(input, "W-APP-UNKNOWN", "warning", `application "${application.application}" of ${input.id} (from ${application.origin}) is not declared in the configuration; it is kept as written`, "Declare the application under applications in concordance.yaml, or fix the source, the rule or the frontmatter that sets it."),
28
+ ];
29
+ }
30
+ function domainFindings(input, filed) {
31
+ const { domain } = input;
32
+ if (!domain.declared) {
33
+ return [
34
+ finding(input, "W-DOMAIN-UNKNOWN", "warning", `frontmatter domain "${domain.domain}" of ${input.id} is not declared in the configuration; it is kept as written`, "Declare the domain under domains in concordance.yaml, or name a declared domain by its id or its id path."),
35
+ ];
36
+ }
37
+ if (domain.domain !== UNCLASSIFIED_DOMAIN || !filed)
38
+ return [];
39
+ return [
40
+ finding(input, "W-DOMAIN-UNCLASSIFIED", "info", `${input.id} matches no declared domain`, "Add a folder or a glob to the domain in concordance.yaml, or set domain in the note's frontmatter."),
41
+ ];
42
+ }
43
+ /** The filing findings of one entity; containers are exempt from the missing-application and unclassified ones. */
44
+ export function filingFindings(input) {
45
+ const filed = input.profile.types[input.type]?.group !== CONTAINER_GROUP;
46
+ return [...applicationFindings(input, filed), ...domainFindings(input, filed)];
47
+ }
48
+ //# sourceMappingURL=filing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filing.js","sourceRoot":"","sources":["../src/filing.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,mBAAmB,EAAuB,MAAM,cAAc,CAAC;AAExE,gGAAgG;AAChG,MAAM,eAAe,GAAG,WAAW,CAAC;AAapC,SAAS,OAAO,CACd,KAAkB,EAClB,KAAa,EACb,QAA6B,EAC7B,OAAe,EACf,WAAmB;IAEnB,OAAO;QACL,KAAK;QACL,QAAQ;QACR,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,MAAM,EAAE,KAAK,CAAC,EAAE;QAChB,OAAO;QACP,WAAW;KACZ,CAAC;AACJ,CAAC;AAED,SAAS,mBAAmB,CAAC,KAAkB,EAAE,KAAc;IAC7D,MAAM,EAAE,WAAW,EAAE,GAAG,KAAK,CAAC;IAC9B,IAAI,WAAW,CAAC,WAAW,KAAK,SAAS,EAAE,CAAC;QAC1C,IAAI,CAAC,KAAK;YAAE,OAAO,EAAE,CAAC;QACtB,OAAO;YACL,OAAO,CACL,KAAK,EACL,eAAe,EACf,SAAS,EACT,GAAG,KAAK,CAAC,EAAE,6BAA6B,EACxC,gFAAgF,CACjF;SACF,CAAC;IACJ,CAAC;IACD,IAAI,WAAW,CAAC,QAAQ;QAAE,OAAO,EAAE,CAAC;IACpC,OAAO;QACL,OAAO,CACL,KAAK,EACL,eAAe,EACf,SAAS,EACT,gBAAgB,WAAW,CAAC,WAAW,QAAQ,KAAK,CAAC,EAAE,UAAU,WAAW,CAAC,MAAM,+DAA+D,EAClJ,8HAA8H,CAC/H;KACF,CAAC;AACJ,CAAC;AAED,SAAS,cAAc,CAAC,KAAkB,EAAE,KAAc;IACxD,MAAM,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC;IACzB,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;QACrB,OAAO;YACL,OAAO,CACL,KAAK,EACL,kBAAkB,EAClB,SAAS,EACT,uBAAuB,MAAM,CAAC,MAAM,QAAQ,KAAK,CAAC,EAAE,8DAA8D,EAClH,2GAA2G,CAC5G;SACF,CAAC;IACJ,CAAC;IACD,IAAI,MAAM,CAAC,MAAM,KAAK,mBAAmB,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,CAAC;IAC/D,OAAO;QACL,OAAO,CACL,KAAK,EACL,uBAAuB,EACvB,MAAM,EACN,GAAG,KAAK,CAAC,EAAE,6BAA6B,EACxC,oGAAoG,CACrG;KACF,CAAC;AACJ,CAAC;AAED,mHAAmH;AACnH,MAAM,UAAU,cAAc,CAAC,KAAkB;IAC/C,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,KAAK,KAAK,eAAe,CAAC;IACzE,OAAO,CAAC,GAAG,mBAAmB,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,GAAG,cAAc,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;AACjF,CAAC"}
@@ -0,0 +1,3 @@
1
+ /** A frontmatter scalar as the cascades read it: a string as written, anything else serialised so that findings show it. */
2
+ export declare function declaredString(value: unknown): string | undefined;
3
+ //# sourceMappingURL=frontmatter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frontmatter.d.ts","sourceRoot":"","sources":["../src/frontmatter.ts"],"names":[],"mappings":"AAAA,4HAA4H;AAC5H,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAGjE"}
@@ -0,0 +1,7 @@
1
+ /** A frontmatter scalar as the cascades read it: a string as written, anything else serialised so that findings show it. */
2
+ export function declaredString(value) {
3
+ if (value === undefined)
4
+ return undefined;
5
+ return typeof value === "string" ? value : JSON.stringify(value);
6
+ }
7
+ //# sourceMappingURL=frontmatter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frontmatter.js","sourceRoot":"","sources":["../src/frontmatter.ts"],"names":[],"mappings":"AAAA,4HAA4H;AAC5H,MAAM,UAAU,cAAc,CAAC,KAAc;IAC3C,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC1C,OAAO,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;AACnE,CAAC"}