@profile-as-code/core 0.1.1 → 0.1.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.
Files changed (60) hide show
  1. package/dist/achievements.d.ts +83 -0
  2. package/dist/achievements.d.ts.map +1 -0
  3. package/dist/achievements.js +207 -0
  4. package/dist/achievements.js.map +1 -0
  5. package/dist/cite.d.ts +91 -0
  6. package/dist/cite.d.ts.map +1 -0
  7. package/dist/cite.js +440 -0
  8. package/dist/cite.js.map +1 -0
  9. package/dist/completeness.d.ts +2 -2
  10. package/dist/completeness.d.ts.map +1 -1
  11. package/dist/completeness.js +22 -5
  12. package/dist/completeness.js.map +1 -1
  13. package/dist/generated/ai-policy.gen.d.ts +36 -0
  14. package/dist/generated/ai-policy.gen.d.ts.map +1 -0
  15. package/dist/generated/ai-policy.gen.js +31 -0
  16. package/dist/generated/ai-policy.gen.js.map +1 -0
  17. package/dist/generated/platforms.gen.d.ts +8 -0
  18. package/dist/generated/platforms.gen.d.ts.map +1 -1
  19. package/dist/generated/platforms.gen.js +68 -0
  20. package/dist/generated/platforms.gen.js.map +1 -1
  21. package/dist/generated/schema.zod.d.ts +57 -0
  22. package/dist/generated/schema.zod.d.ts.map +1 -1
  23. package/dist/generated/schema.zod.js +6 -1
  24. package/dist/generated/schema.zod.js.map +1 -1
  25. package/dist/geo.d.ts +39 -4
  26. package/dist/geo.d.ts.map +1 -1
  27. package/dist/geo.js +127 -43
  28. package/dist/geo.js.map +1 -1
  29. package/dist/index.d.ts +39 -3
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +26 -3
  32. package/dist/index.js.map +1 -1
  33. package/dist/load.d.ts.map +1 -1
  34. package/dist/load.js +17 -0
  35. package/dist/load.js.map +1 -1
  36. package/dist/normalize.d.ts +57 -0
  37. package/dist/normalize.d.ts.map +1 -0
  38. package/dist/normalize.js +133 -0
  39. package/dist/normalize.js.map +1 -0
  40. package/dist/presence.d.ts +113 -0
  41. package/dist/presence.d.ts.map +1 -0
  42. package/dist/presence.js +229 -0
  43. package/dist/presence.js.map +1 -0
  44. package/dist/registry.d.ts +6 -3
  45. package/dist/registry.d.ts.map +1 -1
  46. package/dist/registry.js +8 -4
  47. package/dist/registry.js.map +1 -1
  48. package/dist/structured-data.d.ts +4 -3
  49. package/dist/structured-data.d.ts.map +1 -1
  50. package/dist/structured-data.js +72 -7
  51. package/dist/structured-data.js.map +1 -1
  52. package/dist/types.d.ts +49 -10
  53. package/dist/types.d.ts.map +1 -1
  54. package/dist/types.js +16 -2
  55. package/dist/types.js.map +1 -1
  56. package/package.json +1 -1
  57. package/dist/paste-source.d.ts +0 -14
  58. package/dist/paste-source.d.ts.map +0 -1
  59. package/dist/paste-source.js +0 -17
  60. package/dist/paste-source.js.map +0 -1
@@ -0,0 +1,83 @@
1
+ /**
2
+ * achievements.ts — the career impact log (D24, SPEC §23, issue #446).
3
+ *
4
+ * The contract models what a person DID (`experience[].highlights`) and what can
5
+ * be PROVEN (`projects[].evidence[]`), but not what CHANGED. This module owns the
6
+ * one thing the block cannot store: the **status**, DERIVED from the numbers at
7
+ * read time.
8
+ *
9
+ * achievementStatus(a) planned | measured | achieved | partial — or undefined
10
+ * for a milestone (a point event has no progress) and for
11
+ * a metric that declares neither `value` nor `target`
12
+ * (nothing to plan, nothing to measure).
13
+ * achievementScope(list) the counters the CLI's `--json` `scope` reports, so
14
+ * "nothing measured" is distinguishable from "clean"
15
+ * (the `pasco audit` shape discipline, §7.1).
16
+ * checkAchievementIntegrity(profile) the post-Zod contract checks the generated
17
+ * Zod cannot express (D12 fail class): duplicate ids,
18
+ * dangling `refs`/`artifactId`/`credentialId`, the
19
+ * `kind` ↔ `metric` invariant, and the §18.5 leak (a
20
+ * PUBLIC achievement pointing at a private credential or
21
+ * artifact). `load()` throws on any violation.
22
+ *
23
+ * Offline and deterministic (D19): no network, no clock, no AI. `direction` is
24
+ * OPTIONAL — absent means `increase`, a DECLARED default (never inferred from the
25
+ * baseline: a `72 → 4` row without a direction is `decrease` only if it says so).
26
+ */
27
+ import { type RegistryIssue } from "./registry.js";
28
+ import type { LoadedProfile, Profile } from "./types.js";
29
+ /** A row of the impact log (schema v1.16, D24). */
30
+ export type Achievement = NonNullable<LoadedProfile["achievements"]>[number];
31
+ /** The numeric shape of a `metric` row. */
32
+ export type AchievementMetric = NonNullable<Achievement["metric"]>;
33
+ /** HOW the result is known — the honesty field, not a gate. */
34
+ export type AchievementBasis = NonNullable<Achievement["basis"]>;
35
+ /** Which way is better. */
36
+ export type AchievementDirection = NonNullable<AchievementMetric["direction"]>;
37
+ /** The DERIVED status. `planned` is a goal; `measured` a number with no goal. */
38
+ export type AchievementStatus = "planned" | "measured" | "achieved" | "partial";
39
+ /** Absent `direction` = `increase`. Declared here once; every reader calls it. */
40
+ export declare const DEFAULT_ACHIEVEMENT_DIRECTION: AchievementDirection;
41
+ /** The counters `pasco achievements --json` reports under `scope`. */
42
+ export interface AchievementScope {
43
+ total: number;
44
+ planned: number;
45
+ measured: number;
46
+ achieved: number;
47
+ partial: number;
48
+ }
49
+ /**
50
+ * The DERIVED status of one row (never stored, §23):
51
+ *
52
+ * target, no value planned
53
+ * value, no target measured
54
+ * both, direction met achieved
55
+ * both, direction not met partial
56
+ * kind: milestone undefined (a point event has no progress)
57
+ * metric with neither number undefined (nothing to plan, nothing to measure)
58
+ *
59
+ * A `kind: milestone` row carrying a `metric` is a contract violation that
60
+ * `checkAchievementIntegrity` rejects at load; this function does not guess a
61
+ * status for it.
62
+ */
63
+ export declare function achievementStatus(achievement: Achievement): AchievementStatus | undefined;
64
+ /** The `scope` counters of a log: total plus one bucket per derived status. */
65
+ export declare function achievementScope(achievements: readonly Achievement[]): AchievementScope;
66
+ /** The blocks a `refs[]` entry may address — the schema's `refs` alternation and
67
+ * this list must agree; `tests/achievements.test.ts` pins them against each
68
+ * other (F6 of the pre-merge review: a hand-copy with no guard let a future
69
+ * block added only to the schema make a valid ref fail `load` as dangling). */
70
+ export declare const ACHIEVEMENT_REF_BLOCKS: readonly ["experience", "projects", "publications", "education", "press", "credentials", "artifacts"];
71
+ /**
72
+ * Post-Zod contract checks for the impact log (load fails on any of them, D12):
73
+ *
74
+ * - duplicate `achievements[].id`;
75
+ * - a `refs[]`, `artifactId` or `credentialId` that does not resolve (same class
76
+ * as the registry cross-refs: a dangling pointer is never silently ignored);
77
+ * - `kind: metric` without `metric` / `kind: milestone` with one — the `if/then`
78
+ * the issue rejected is enforced here and in the CLI, not in JSON Schema;
79
+ * - a PUBLIC achievement pointing at a `private`/`unlisted` credential or artifact
80
+ * (the §18.5 leak rule, same one `projects[].artifactRefs` answers to).
81
+ */
82
+ export declare function checkAchievementIntegrity(profile: Profile): RegistryIssue[];
83
+ //# sourceMappingURL=achievements.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"achievements.d.ts","sourceRoot":"","sources":["../src/achievements.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EAAiB,KAAK,aAAa,EAAE,MAAM,eAAe,CAAC;AAClE,OAAO,KAAK,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAEzD,mDAAmD;AACnD,MAAM,MAAM,WAAW,GAAG,WAAW,CAAC,aAAa,CAAC,cAAc,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;AAC7E,2CAA2C;AAC3C,MAAM,MAAM,iBAAiB,GAAG,WAAW,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAC;AACnE,+DAA+D;AAC/D,MAAM,MAAM,gBAAgB,GAAG,WAAW,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC;AACjE,2BAA2B;AAC3B,MAAM,MAAM,oBAAoB,GAAG,WAAW,CAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC,CAAC;AAC/E,iFAAiF;AACjF,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,UAAU,GAAG,UAAU,GAAG,SAAS,CAAC;AAEhF,kFAAkF;AAClF,eAAO,MAAM,6BAA6B,EAAE,oBAAiC,CAAC;AAE9E,sEAAsE;AACtE,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAiBD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,WAAW,GAAG,iBAAiB,GAAG,SAAS,CAUzF;AAED,+EAA+E;AAC/E,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,SAAS,WAAW,EAAE,GAAG,gBAAgB,CAavF;AAED;;;gFAGgF;AAChF,eAAO,MAAM,sBAAsB,YACjC,YAAY,EACZ,UAAU,EACV,cAAc,EACd,WAAW,EACX,OAAO,EACP,aAAa,EACb,WAAW,CACH,CAAC;AA2BX;;;;;;;;;;GAUG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,OAAO,GAAG,aAAa,EAAE,CAsH3E"}
@@ -0,0 +1,207 @@
1
+ /**
2
+ * achievements.ts — the career impact log (D24, SPEC §23, issue #446).
3
+ *
4
+ * The contract models what a person DID (`experience[].highlights`) and what can
5
+ * be PROVEN (`projects[].evidence[]`), but not what CHANGED. This module owns the
6
+ * one thing the block cannot store: the **status**, DERIVED from the numbers at
7
+ * read time.
8
+ *
9
+ * achievementStatus(a) planned | measured | achieved | partial — or undefined
10
+ * for a milestone (a point event has no progress) and for
11
+ * a metric that declares neither `value` nor `target`
12
+ * (nothing to plan, nothing to measure).
13
+ * achievementScope(list) the counters the CLI's `--json` `scope` reports, so
14
+ * "nothing measured" is distinguishable from "clean"
15
+ * (the `pasco audit` shape discipline, §7.1).
16
+ * checkAchievementIntegrity(profile) the post-Zod contract checks the generated
17
+ * Zod cannot express (D12 fail class): duplicate ids,
18
+ * dangling `refs`/`artifactId`/`credentialId`, the
19
+ * `kind` ↔ `metric` invariant, and the §18.5 leak (a
20
+ * PUBLIC achievement pointing at a private credential or
21
+ * artifact). `load()` throws on any violation.
22
+ *
23
+ * Offline and deterministic (D19): no network, no clock, no AI. `direction` is
24
+ * OPTIONAL — absent means `increase`, a DECLARED default (never inferred from the
25
+ * baseline: a `72 → 4` row without a direction is `decrease` only if it says so).
26
+ */
27
+ import { isPublicEntry } from "./registry.js";
28
+ /** Absent `direction` = `increase`. Declared here once; every reader calls it. */
29
+ export const DEFAULT_ACHIEVEMENT_DIRECTION = "increase";
30
+ /**
31
+ * The DIRECTION verdict for a row that has both `value` and `target`.
32
+ * `increase`: value >= target · `decrease`: value <= target · `maintain`: equal.
33
+ */
34
+ function directionMet(value, target, direction) {
35
+ switch (direction) {
36
+ case "increase":
37
+ return value >= target;
38
+ case "decrease":
39
+ return value <= target;
40
+ case "maintain":
41
+ return value === target;
42
+ }
43
+ }
44
+ /**
45
+ * The DERIVED status of one row (never stored, §23):
46
+ *
47
+ * target, no value planned
48
+ * value, no target measured
49
+ * both, direction met achieved
50
+ * both, direction not met partial
51
+ * kind: milestone undefined (a point event has no progress)
52
+ * metric with neither number undefined (nothing to plan, nothing to measure)
53
+ *
54
+ * A `kind: milestone` row carrying a `metric` is a contract violation that
55
+ * `checkAchievementIntegrity` rejects at load; this function does not guess a
56
+ * status for it.
57
+ */
58
+ export function achievementStatus(achievement) {
59
+ if (achievement.kind !== "metric")
60
+ return undefined;
61
+ const metric = achievement.metric;
62
+ if (metric === undefined)
63
+ return undefined;
64
+ const { value, target } = metric;
65
+ if (target !== undefined && value === undefined)
66
+ return "planned";
67
+ if (value !== undefined && target === undefined)
68
+ return "measured";
69
+ if (value === undefined || target === undefined)
70
+ return undefined;
71
+ const direction = metric.direction ?? DEFAULT_ACHIEVEMENT_DIRECTION;
72
+ return directionMet(value, target, direction) ? "achieved" : "partial";
73
+ }
74
+ /** The `scope` counters of a log: total plus one bucket per derived status. */
75
+ export function achievementScope(achievements) {
76
+ const scope = {
77
+ total: achievements.length,
78
+ planned: 0,
79
+ measured: 0,
80
+ achieved: 0,
81
+ partial: 0,
82
+ };
83
+ for (const achievement of achievements) {
84
+ const status = achievementStatus(achievement);
85
+ if (status !== undefined)
86
+ scope[status] += 1;
87
+ }
88
+ return scope;
89
+ }
90
+ /** The blocks a `refs[]` entry may address — the schema's `refs` alternation and
91
+ * this list must agree; `tests/achievements.test.ts` pins them against each
92
+ * other (F6 of the pre-merge review: a hand-copy with no guard let a future
93
+ * block added only to the schema make a valid ref fail `load` as dangling). */
94
+ export const ACHIEVEMENT_REF_BLOCKS = [
95
+ "experience",
96
+ "projects",
97
+ "publications",
98
+ "education",
99
+ "press",
100
+ "credentials",
101
+ "artifacts",
102
+ ];
103
+ /** `block:id` ids the profile actually declares, per block. */
104
+ function declaredIds(profile) {
105
+ const rows = {
106
+ experience: profile.experience,
107
+ projects: profile.projects,
108
+ publications: profile.publications,
109
+ education: profile.education,
110
+ press: profile.press,
111
+ credentials: profile.credentials,
112
+ artifacts: profile.artifacts,
113
+ };
114
+ const map = new Map();
115
+ for (const block of ACHIEVEMENT_REF_BLOCKS) {
116
+ const ids = new Set();
117
+ for (const row of rows[block] ?? [])
118
+ if (row.id !== undefined)
119
+ ids.add(row.id);
120
+ map.set(block, ids);
121
+ }
122
+ return map;
123
+ }
124
+ function issue(path, message) {
125
+ return { path, message };
126
+ }
127
+ /**
128
+ * Post-Zod contract checks for the impact log (load fails on any of them, D12):
129
+ *
130
+ * - duplicate `achievements[].id`;
131
+ * - a `refs[]`, `artifactId` or `credentialId` that does not resolve (same class
132
+ * as the registry cross-refs: a dangling pointer is never silently ignored);
133
+ * - `kind: metric` without `metric` / `kind: milestone` with one — the `if/then`
134
+ * the issue rejected is enforced here and in the CLI, not in JSON Schema;
135
+ * - a PUBLIC achievement pointing at a `private`/`unlisted` credential or artifact
136
+ * (the §18.5 leak rule, same one `projects[].artifactRefs` answers to).
137
+ */
138
+ export function checkAchievementIntegrity(profile) {
139
+ const issues = [];
140
+ const achievements = profile.achievements ?? [];
141
+ const ids = declaredIds(profile);
142
+ const credentials = profile.credentials ?? [];
143
+ const artifacts = profile.artifacts ?? [];
144
+ const seen = new Map();
145
+ for (const [index, achievement] of achievements.entries()) {
146
+ const first = seen.get(achievement.id);
147
+ if (first !== undefined) {
148
+ issues.push(issue(`achievements[${index}].id`, `duplicate achievement id "${achievement.id}" (first used at achievements[${first}]) — achievement ids are unique per block (load fails, D12 fail class)`));
149
+ }
150
+ else {
151
+ seen.set(achievement.id, index);
152
+ }
153
+ // kind ↔ metric (§23 — the two shapes are exclusive; `if/then` is not used).
154
+ if (achievement.kind === "metric" && achievement.metric === undefined) {
155
+ issues.push(issue(`achievements[${index}].metric`, `kind: metric requires a "metric" block (unit is required; value/target/baseline are the numbers) — without it there is no status to derive`));
156
+ }
157
+ if (achievement.kind === "milestone" && achievement.metric !== undefined) {
158
+ issues.push(issue(`achievements[${index}].metric`, `kind: milestone must NOT carry a "metric" block — a point event has no progress to derive; use kind: metric, or move the numbers into the statement`));
159
+ }
160
+ // refs[] — a dangling pointer is a load failure, not a warning.
161
+ for (const [refIndex, ref] of (achievement.refs ?? []).entries()) {
162
+ const separator = ref.indexOf(":");
163
+ const block = ref.slice(0, separator);
164
+ const id = ref.slice(separator + 1);
165
+ if (!ids.get(block)?.has(id)) {
166
+ issues.push(issue(`achievements[${index}].refs[${refIndex}]`, `references ${block}[].id "${id}" which does not exist (dangling cross-ref, load fails)`));
167
+ }
168
+ }
169
+ if (achievement.credentialId !== undefined) {
170
+ const credential = credentials.find((c) => c.id === achievement.credentialId);
171
+ if (credential === undefined) {
172
+ issues.push(issue(`achievements[${index}].credentialId`, `references credentials[].id "${achievement.credentialId}" which does not exist (dangling cross-ref, load fails)`));
173
+ }
174
+ else if (achievement.visibility === "public" && !isPublicEntry(credential)) {
175
+ issues.push(issue(`achievements[${index}].credentialId`, `a public achievement attests credential "${credential.id}" with visibility "${credential.visibility}" — public output renders only public entries (§18.5); make it public, or make the achievement non-public`));
176
+ }
177
+ }
178
+ if (achievement.artifactId !== undefined) {
179
+ const artifact = artifacts.find((a) => a.id === achievement.artifactId);
180
+ if (artifact === undefined) {
181
+ issues.push(issue(`achievements[${index}].artifactId`, `references artifacts[].id "${achievement.artifactId}" which does not exist (dangling cross-ref, load fails)`));
182
+ }
183
+ else if (achievement.visibility === "public" && !isPublicEntry(artifact)) {
184
+ issues.push(issue(`achievements[${index}].artifactId`, `a public achievement points at artifact "${artifact.id}" with visibility "${artifact.visibility}" — public output renders only public entries (§18.5); make it public, or make the achievement non-public`));
185
+ }
186
+ }
187
+ // A public achievement that references a private entry THROUGH refs[] is the
188
+ // same leak: the ref is what a renderer groups the row by.
189
+ if (achievement.visibility === "public") {
190
+ for (const [refIndex, ref] of (achievement.refs ?? []).entries()) {
191
+ const separator = ref.indexOf(":");
192
+ const block = ref.slice(0, separator);
193
+ const id = ref.slice(separator + 1);
194
+ const target = block === "credentials"
195
+ ? credentials.find((c) => c.id === id)
196
+ : block === "artifacts"
197
+ ? artifacts.find((a) => a.id === id)
198
+ : undefined;
199
+ if (target !== undefined && !isPublicEntry(target)) {
200
+ issues.push(issue(`achievements[${index}].refs[${refIndex}]`, `a public achievement references ${block}[].id "${id}" with visibility "${target.visibility}" — public output renders only public entries (§18.5)`));
201
+ }
202
+ }
203
+ }
204
+ }
205
+ return issues;
206
+ }
207
+ //# sourceMappingURL=achievements.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"achievements.js","sourceRoot":"","sources":["../src/achievements.ts"],"names":[],"mappings":"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"}
package/dist/cite.d.ts ADDED
@@ -0,0 +1,91 @@
1
+ /**
2
+ * cite.ts — the deterministic citation matcher (SPEC §7.4, issue #291).
3
+ *
4
+ * The engine answers ONE question about an answer-engine record: did this saved
5
+ * answer cite this entity? It is a pure function over the SSOT and the answer
6
+ * bytes — no network, no model, no clock (D19) — because the alternative
7
+ * (substring matching) is why GEO reporting overreports, and the only fix is a
8
+ * matcher that says exactly which text it matched, why, and when it refuses to
9
+ * guess.
10
+ *
11
+ * The vocabulary is the workspace's DECLARED data, never world knowledge:
12
+ * name identity.name
13
+ * alias identity.orgName (the one declared alternate name the schema has)
14
+ * handle identity.handle (its final token, '@' stripped) + socialHandles
15
+ * rows with status: claimed
16
+ * hosts the role: identity URL and the sameAs set (structured-data.ts —
17
+ * one derivation, never a second)
18
+ * partial a contiguous multi-token prefix of the name (low confidence)
19
+ *
20
+ * Normalization and word-boundary matching live in `normalize.ts`. It is
21
+ * `cite-check`'s fold, not `geoCheck`'s: `geoCheck` keeps its own case-only
22
+ * comparison so this matcher cannot move a published GEO score (§8).
23
+ *
24
+ * Guards make a text hit NOT a citation (`cited: false`): a shorter term
25
+ * contained in a longer declared one (`longer-name`), a one-word name with no
26
+ * declared disambiguator in its sentence (`ambiguous-term` + `no-qualifier`), and
27
+ * a mention inside a contrast clause (`contrast-context`). `competing:<phrase>`
28
+ * never decides on its own — it only names the longer reading the text carries
29
+ * when the hit is already ambiguous, so ordinary prose cannot become a
30
+ * competitor through an adjacent capital. `confidence: low` is never citable
31
+ * downstream (declared in §7.4).
32
+ */
33
+ import type { Profile } from "./types.js";
34
+ /** The rung a span was matched by — one kind per ladder rung (SPEC §7.4). */
35
+ export type CiteSpanKind = "citation-url" | "name" | "alias" | "handle" | "partial";
36
+ /** The best surviving rung. `low` is never a citation in a downstream report. */
37
+ export type CiteConfidence = "high" | "medium" | "low";
38
+ /** One citation row of the runner's input contract. */
39
+ export interface CiteCitation {
40
+ url: string;
41
+ title?: string;
42
+ }
43
+ /**
44
+ * One saved answer (the runner's contract, never produced here). The matcher
45
+ * reads `answer` + `citations`; the rest is carried through for a report.
46
+ */
47
+ export interface CiteAnswer {
48
+ engine?: string | null;
49
+ askedAt?: string | null;
50
+ question?: string | null;
51
+ answer: string;
52
+ citations: CiteCitation[];
53
+ }
54
+ /** A matched span: a character range in `answer`, or a citation pointer. */
55
+ export interface CiteSpan {
56
+ /** Original offset (inclusive). 0 for a `citation-url` span (not in the text). */
57
+ start: number;
58
+ /** Original offset (exclusive). 0 for a `citation-url` span. */
59
+ end: number;
60
+ kind: CiteSpanKind;
61
+ /** The citation URL — only on `citation-url` spans. */
62
+ url?: string;
63
+ }
64
+ /** Per-answer result. Never aggregated by the engine (SPEC §7.4). */
65
+ export interface CiteMatch {
66
+ cited: boolean;
67
+ confidence: CiteConfidence;
68
+ spans: CiteSpan[];
69
+ reasons: string[];
70
+ }
71
+ /**
72
+ * The vocabulary the matcher resolved from the workspace. Exposed in `--json`
73
+ * so a report can say WHAT was matched without re-reading the SSOT.
74
+ */
75
+ export interface CiteVocabulary {
76
+ name: string;
77
+ aliases: string[];
78
+ handles: string[];
79
+ partials: string[];
80
+ hosts: string[];
81
+ }
82
+ /** The public vocabulary of a workspace (the `entity` block of `--json`). */
83
+ export declare function citeVocabulary(profile: Profile): CiteVocabulary;
84
+ /**
85
+ * Match one saved answer against the workspace's declared entity (SPEC §7.4).
86
+ * Pure: the same profile + answer bytes always produce the same result.
87
+ */
88
+ export declare function citeMatch(profile: Profile, answer: CiteAnswer): CiteMatch;
89
+ /** Exported for tests: the declared host of a URL under the matcher's rule. */
90
+ export declare function citationHost(url: string): string | undefined;
91
+ //# sourceMappingURL=cite.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cite.d.ts","sourceRoot":"","sources":["../src/cite.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAMH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE1C,6EAA6E;AAC7E,MAAM,MAAM,YAAY,GAAG,cAAc,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,CAAC;AAEpF,iFAAiF;AACjF,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;AAEvD,uDAAuD;AACvD,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,MAAM,WAAW,UAAU;IACzB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,YAAY,EAAE,CAAC;CAC3B;AAED,4EAA4E;AAC5E,MAAM,WAAW,QAAQ;IACvB,kFAAkF;IAClF,KAAK,EAAE,MAAM,CAAC;IACd,gEAAgE;IAChE,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,YAAY,CAAC;IACnB,uDAAuD;IACvD,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,qEAAqE;AACrE,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,OAAO,CAAC;IACf,UAAU,EAAE,cAAc,CAAC;IAC3B,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAsND,6EAA6E;AAC7E,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,GAAG,cAAc,CAE/D;AAgFD;;;GAGG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,GAAG,SAAS,CA0JzE;AAaD,+EAA+E;AAC/E,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAE5D"}