glossarist 0.4.42 → 0.4.43

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 (82) hide show
  1. package/package.json +1 -1
  2. package/src/{compiled-format.js → compiled-format.ts} +1 -0
  3. package/src/{concept-collection.js → concept-collection.ts} +1 -0
  4. package/src/{concept-diff.js → concept-diff.ts} +1 -0
  5. package/src/{concept-parser.js → concept-parser.ts} +1 -0
  6. package/src/{concept-reader.js → concept-reader.ts} +1 -0
  7. package/src/{concept-serializer.js → concept-serializer.ts} +1 -0
  8. package/src/{concept-writer.js → concept-writer.ts} +1 -0
  9. package/src/{dataset-asset.js → dataset-asset.ts} +1 -0
  10. package/src/diff/change.ts +175 -0
  11. package/src/diff/{collection-diff.js → collection-diff.ts} +1 -0
  12. package/src/diff/{concept-diff.js → concept-diff.ts} +1 -0
  13. package/src/diff/{diff-patch.js → diff-patch.ts} +1 -0
  14. package/src/diff/{diff-renderer.js → diff-renderer.ts} +1 -0
  15. package/src/diff/{index.js → index.ts} +1 -0
  16. package/src/diff/list-diff.ts +231 -0
  17. package/src/{entity-directory.js → entity-directory.ts} +1 -0
  18. package/src/{errors.js → errors.ts} +1 -0
  19. package/src/{gcr-reader.js → gcr-reader.ts} +1 -0
  20. package/src/{gcr-writer.js → gcr-writer.ts} +1 -0
  21. package/src/{index.js → index.ts} +1 -0
  22. package/src/{managed-concept-collection.js → managed-concept-collection.ts} +1 -0
  23. package/src/migration/{hyperedge-migrator.js → hyperedge-migrator.ts} +1 -0
  24. package/src/rdf/{agents-emitter.js → agents-emitter.ts} +1 -0
  25. package/src/rdf/{bibliography-emitter.js → bibliography-emitter.ts} +1 -0
  26. package/src/rdf/{bnode-id.js → bnode-id.ts} +1 -0
  27. package/src/rdf/{build-activity-emitter.js → build-activity-emitter.ts} +1 -0
  28. package/src/rdf/{curie.js → curie.ts} +1 -0
  29. package/src/rdf/{dataset-emitter.js → dataset-emitter.ts} +1 -0
  30. package/src/rdf/{deterministic-id.js → deterministic-id.ts} +1 -0
  31. package/src/rdf/{document-writer.js → document-writer.ts} +1 -0
  32. package/src/rdf/{gloss-concept.js → gloss-concept.ts} +1 -0
  33. package/src/rdf/{gloss-designation.js → gloss-designation.ts} +1 -0
  34. package/src/rdf/{gloss-detailed-definition.js → gloss-detailed-definition.ts} +1 -0
  35. package/src/rdf/{gloss-generic-hyperedge.js → gloss-generic-hyperedge.ts} +1 -0
  36. package/src/rdf/{gloss-localized-concept.js → gloss-localized-concept.ts} +1 -0
  37. package/src/rdf/{gloss-non-verbal-rep.js → gloss-non-verbal-rep.ts} +1 -0
  38. package/src/rdf/{gloss-partitive-member.js → gloss-partitive-member.ts} +1 -0
  39. package/src/rdf/{gloss-partitive-relation.js → gloss-partitive-relation.ts} +1 -0
  40. package/src/rdf/{gloss-source.js → gloss-source.ts} +1 -0
  41. package/src/rdf/{group-emitter.js → group-emitter.ts} +1 -0
  42. package/src/rdf/{image-variant-emitter.js → image-variant-emitter.ts} +1 -0
  43. package/src/rdf/{index.js → index.ts} +1 -0
  44. package/src/rdf/{normalize-enum.js → normalize-enum.ts} +1 -0
  45. package/src/rdf/{prefixes.js → prefixes.ts} +1 -0
  46. package/src/rdf/{provenance-emitter.js → provenance-emitter.ts} +1 -0
  47. package/src/rdf/{sections-builder.js → sections-builder.ts} +1 -0
  48. package/src/rdf/{shacl-entry.js → shacl-entry.ts} +1 -0
  49. package/src/rdf/{shacl.js → shacl.ts} +1 -0
  50. package/src/rdf/{terms.js → terms.ts} +1 -0
  51. package/src/rdf/{version-emitter.js → version-emitter.ts} +1 -0
  52. package/src/rdf/{vocabulary-emitter.js → vocabulary-emitter.ts} +1 -0
  53. package/src/rdf/{write-turtle-sync.js → write-turtle-sync.ts} +1 -0
  54. package/src/{reference-mention.js → reference-mention.ts} +1 -0
  55. package/src/{reference-resolver.js → reference-resolver.ts} +1 -0
  56. package/src/{render-classification.js → render-classification.ts} +1 -0
  57. package/src/{sort.js → sort.ts} +1 -0
  58. package/src/transforms/{concept-to-gloss.transform.js → concept-to-gloss.transform.ts} +1 -0
  59. package/src/transforms/index.ts +2 -0
  60. package/src/{uuid.js → uuid.ts} +1 -0
  61. package/src/{v1-reader.js → v1-reader.ts} +1 -0
  62. package/src/validators/{asset-index.js → asset-index.ts} +1 -0
  63. package/src/validators/{binary-has-part-redundancy-rule.js → binary-has-part-redundancy-rule.ts} +1 -0
  64. package/src/validators/{concept-validator.js → concept-validator.ts} +1 -0
  65. package/src/validators/{delimiting-coherence-rule.js → delimiting-coherence-rule.ts} +1 -0
  66. package/src/validators/{external-concept-shape-rule.js → external-concept-shape-rule.ts} +1 -0
  67. package/src/validators/{gcr-validator.js → gcr-validator.ts} +1 -0
  68. package/src/validators/{hyperedge-coherence-rule.js → hyperedge-coherence-rule.ts} +1 -0
  69. package/src/validators/{index.js → index.ts} +1 -0
  70. package/src/validators/{partitive-relation-coherence-rule.js → partitive-relation-coherence-rule.ts} +1 -0
  71. package/src/validators/{ref-keys.js → ref-keys.ts} +1 -0
  72. package/src/validators/{register-validator.js → register-validator.ts} +1 -0
  73. package/src/validators/{relationship-type-rule.js → relationship-type-rule.ts} +1 -0
  74. package/src/validators/{sourced-from-locality-rule.js → sourced-from-locality-rule.ts} +1 -0
  75. package/src/validators/{v3-rules.js → v3-rules.ts} +1 -0
  76. package/src/validators/{validation-error.js → validation-error.ts} +1 -0
  77. package/src/validators/{validation-result.js → validation-result.ts} +1 -0
  78. package/src/validators/{validation-rule.js → validation-rule.ts} +1 -0
  79. package/src/diff/change.js +0 -139
  80. package/src/diff/list-diff.js +0 -227
  81. package/src/transforms/index.js +0 -1
  82. /package/src/rdf/{predicates.js → predicates.ts} +0 -0
@@ -1,3 +1,4 @@
1
+ // @ts-nocheck — TEMPORARY during TS migration. TODO(Phase 2e): remove and type fully.
1
2
  // Public RDF layer. Mirrors lib/glossarist/rdf/ in glossarist-ruby.
2
3
  export { PRED, PREFIXES, SKOSXL, WELL_KNOWN } from './prefixes.js';
3
4
  export {
@@ -1,3 +1,4 @@
1
+ // @ts-nocheck — TEMPORARY during TS migration. TODO(Phase 2e): remove and type fully.
1
2
  // Helpers for normalizing enumeration values that get interpolated into
2
3
  // URIs. Source data may contain compound forms ("preferred/admitted") or
3
4
  // path-like values that must be reduced to a single local name before
@@ -1,3 +1,4 @@
1
+ // @ts-nocheck — TEMPORARY during TS migration. TODO(Phase 2e): remove and type fully.
1
2
  // Re-export prefix map and predicate constants generated from the vendored
2
3
  // concept-model JSON-LD context. Downstream code should never duplicate
3
4
  // prefix bindings — always import from here.
@@ -1,3 +1,4 @@
1
+ // @ts-nocheck — TEMPORARY during TS migration. TODO(Phase 2e): remove and type fully.
1
2
  // Provenance emitter — emits prov:wasGeneratedBy + prov:Activity quads
2
3
  // for any resource that needs build/view-time provenance.
3
4
  //
@@ -1,3 +1,4 @@
1
+ // @ts-nocheck — TEMPORARY during TS migration. TODO(Phase 2e): remove and type fully.
1
2
  // Sections builder — transforms a Quad[] into the ClassInstance[]
2
3
  // shape consumed by Vue UI components (RdfInstanceSection,
3
4
  // RdfInstanceHeader, RdfSourcePanel).
@@ -1,3 +1,4 @@
1
+ // @ts-nocheck — TEMPORARY during TS migration. TODO(Phase 2e): remove and type fully.
1
2
  // Node-only SHACL validator entry. Separated from glossarist/rdf so that
2
3
  // browser bundles can import RDF emitters/writers without pulling in
3
4
  // node:fs/promises, node:path, node:url.
@@ -1,3 +1,4 @@
1
+ // @ts-nocheck — TEMPORARY during TS migration. TODO(Phase 2e): remove and type fully.
1
2
  // SHACL validator wrapper. Loads vendored concept-model shapes once and
2
3
  // validates any RDFJS Dataset against them. Mirrors the mechanics of
3
4
  // glossarist-ruby's `Glossarist::Validation::ShaclValidator`.
@@ -1,3 +1,4 @@
1
+ // @ts-nocheck — TEMPORARY during TS migration. TODO(Phase 2e): remove and type fully.
1
2
  // Shared RDF/JS term constructors. Replaces the per-emitter
2
3
  // `const { namedNode, literal, quad } = DataFactory;` pattern that was
3
4
  // repeated across the RDF layer.
@@ -1,3 +1,4 @@
1
+ // @ts-nocheck — TEMPORARY during TS migration. TODO(Phase 2e): remove and type fully.
1
2
  // Version emitter — emits prov:Entity version chain per dataset.
2
3
  // Mirrors concept-browser's version-emitter.ts.
3
4
  //
@@ -1,3 +1,4 @@
1
+ // @ts-nocheck — TEMPORARY during TS migration. TODO(Phase 2e): remove and type fully.
1
2
  // Vocabulary graph emitter — emits SKOS ConceptSchemes for the
2
3
  // enumeration IRIs that glossarist emits in instance data
3
4
  // (gloss:status/*, gloss:entstatus/*, gloss:norm/*, gloss:srcstatus/*,
@@ -1,3 +1,4 @@
1
+ // @ts-nocheck — TEMPORARY during TS migration. TODO(Phase 2e): remove and type fully.
1
2
  // Sync Turtle writer — produces Turtle from RDF/JS Quads without
2
3
  // n3's callback API. Designed for UI use cases (Vue computed, React
3
4
  // render) that need synchronous output.
@@ -1,3 +1,4 @@
1
+ // @ts-nocheck — TEMPORARY during TS migration. TODO(Phase 2e): remove and type fully.
1
2
  /**
2
3
  * Mention parser for {{...}} inline references in concept text.
3
4
  *
@@ -1,3 +1,4 @@
1
+ // @ts-nocheck — TEMPORARY during TS migration. TODO(Phase 2e): remove and type fully.
1
2
  import { ConceptRef } from './models/concept-ref.js';
2
3
  import { parseMention } from './reference-mention.js';
3
4
 
@@ -1,3 +1,4 @@
1
+ // @ts-nocheck — TEMPORARY during TS migration. TODO(Phase 2e): remove and type fully.
1
2
  import { resolveBibliographyRecord, findNonVerbalEntity } from './reference-resolver.js';
2
3
 
3
4
  export class ReferenceClassifier {
@@ -1,3 +1,4 @@
1
+ // @ts-nocheck — TEMPORARY during TS migration. TODO(Phase 2e): remove and type fully.
1
2
  const NATURAL_SORT_RE = /(\d+|\D+)/g;
2
3
  const DIGIT_RE = /^\d+$/;
3
4
 
@@ -1,3 +1,4 @@
1
+ // @ts-nocheck — TEMPORARY during TS migration. TODO(Phase 2e): remove and type fully.
1
2
  // ConceptToGlossTransform — JS port of glossarist-ruby's
2
3
  // `Transforms::ConceptToGlossTransform`. Converts a Concept model into
3
4
  // ontology-faithful RDF (Turtle, JSON-LD, N-Triples).
@@ -0,0 +1,2 @@
1
+ // @ts-nocheck — TEMPORARY during TS migration. TODO(Phase 2e): remove and type fully.
2
+ export { ConceptToGlossTransform } from './concept-to-gloss.transform.js';
@@ -1,3 +1,4 @@
1
+ // @ts-nocheck — TEMPORARY during TS migration. TODO(Phase 2e): remove and type fully.
1
2
  import { createHash } from 'crypto';
2
3
 
3
4
  const NAMESPACE_UUID = '6ba7b810-9dad-11d1-80b4-00c04fd430c8';
@@ -1,3 +1,4 @@
1
+ // @ts-nocheck — TEMPORARY during TS migration. TODO(Phase 2e): remove and type fully.
1
2
  import fs from 'fs';
2
3
  import path from 'path';
3
4
  import * as yaml from 'js-yaml';
@@ -1,3 +1,4 @@
1
+ // @ts-nocheck — TEMPORARY during TS migration. TODO(Phase 2e): remove and type fully.
1
2
  import { DATASET_ASSETS } from '../dataset-asset.js';
2
3
 
3
4
  export class AssetIndex {
@@ -1,3 +1,4 @@
1
+ // @ts-nocheck — TEMPORARY during TS migration. TODO(Phase 2e): remove and type fully.
1
2
  // Warns when binary `has_part` / `is_part_of` edges duplicate
2
3
  // PartitiveHyperedge members, or when a concept has many binary
3
4
  // has_part edges that should be consolidated into a PartitiveHyperedge.
@@ -1,3 +1,4 @@
1
+ // @ts-nocheck — TEMPORARY during TS migration. TODO(Phase 2e): remove and type fully.
1
2
  import { ValidationRule } from './validation-rule.js';
2
3
  import { ValidationResult } from './validation-result.js';
3
4
 
@@ -1,3 +1,4 @@
1
+ // @ts-nocheck — TEMPORARY during TS migration. TODO(Phase 2e): remove and type fully.
1
2
  // Validates delimiting coherence across a concept's PartitiveRelations
2
3
  // per ISO 704:2022.
3
4
  //
@@ -1,3 +1,4 @@
1
+ // @ts-nocheck — TEMPORARY during TS migration. TODO(Phase 2e): remove and type fully.
1
2
  // Validates ExternalConcept shape (status: external).
2
3
  //
3
4
  // Per TODO.partitive-relation-v2 item 06, an ExternalConcept is a
@@ -1,3 +1,4 @@
1
+ // @ts-nocheck — TEMPORARY during TS migration. TODO(Phase 2e): remove and type fully.
1
2
  import * as yaml from 'js-yaml';
2
3
  import { DATASET_ASSETS } from '../dataset-asset.js';
3
4
  import { ValidationResult } from './validation-result.js';
@@ -1,3 +1,4 @@
1
+ // @ts-nocheck — TEMPORARY during TS migration. TODO(Phase 2e): remove and type fully.
1
2
  // Validates hyperedge coherence per ISO 704 / ISO 12620.
2
3
  //
3
4
  // Applies to every AbstractHyperedge subclass (PartitiveHyperedge,
@@ -1,3 +1,4 @@
1
+ // @ts-nocheck — TEMPORARY during TS migration. TODO(Phase 2e): remove and type fully.
1
2
  export { ValidationError } from './validation-error.js';
2
3
  export { ValidationRule } from './validation-rule.js';
3
4
  export { ValidationResult } from './validation-result.js';
@@ -1,3 +1,4 @@
1
+ // @ts-nocheck — TEMPORARY during TS migration. TODO(Phase 2e): remove and type fully.
1
2
  // Deprecated. This rule has been generalized and renamed to
2
3
  // HyperedgeCoherenceRule (see ./hyperedge-coherence-rule.js). The
3
4
  // new rule is type-blind — it validates every AbstractHyperedge
@@ -1,3 +1,4 @@
1
+ // @ts-nocheck — TEMPORARY during TS migration. TODO(Phase 2e): remove and type fully.
1
2
  // Shared identity-key helpers for cross-relation validators.
2
3
  //
3
4
  // Extracted from HyperedgeCoherenceRule and
@@ -1,3 +1,4 @@
1
+ // @ts-nocheck — TEMPORARY during TS migration. TODO(Phase 2e): remove and type fully.
1
2
  import { ValidationResult } from './validation-result.js';
2
3
 
3
4
  export class RegisterValidator {
@@ -1,3 +1,4 @@
1
+ // @ts-nocheck — TEMPORARY during TS migration. TODO(Phase 2e): remove and type fully.
1
2
  import { ValidationRule } from './validation-rule.js';
2
3
  import { RELATIONSHIP_TYPES } from '../models/related-concept.js';
3
4
  import { DESIGNATION_RELATIONSHIP_TYPES } from '../models/designation-relationship.js';
@@ -1,3 +1,4 @@
1
+ // @ts-nocheck — TEMPORARY during TS migration. TODO(Phase 2e): remove and type fully.
1
2
  // Validates that every ConceptSource#sourced_from Citation includes a
2
3
  // locality. Per concept-model commit 9c065df ("sourced_from citations
3
4
  // must include locality"), a sourced_from entry without a locality is
@@ -1,3 +1,4 @@
1
+ // @ts-nocheck — TEMPORARY during TS migration. TODO(Phase 2e): remove and type fully.
1
2
  import { ValidationRule } from './validation-rule.js';
2
3
  import { parseMention } from '../reference-mention.js';
3
4
  import { GraphicalSymbol } from '../models/designation.js';
@@ -1,3 +1,4 @@
1
+ // @ts-nocheck — TEMPORARY during TS migration. TODO(Phase 2e): remove and type fully.
1
2
  export class ValidationError {
2
3
  constructor(path, message, severity = 'error') {
3
4
  this.path = path;
@@ -1,3 +1,4 @@
1
+ // @ts-nocheck — TEMPORARY during TS migration. TODO(Phase 2e): remove and type fully.
1
2
  import { ValidationError } from './validation-error.js';
2
3
 
3
4
  export class ValidationResult {
@@ -1,3 +1,4 @@
1
+ // @ts-nocheck — TEMPORARY during TS migration. TODO(Phase 2e): remove and type fully.
1
2
  export class ValidationRule {
2
3
  constructor(name, severity = 'error') {
3
4
  this.name = name;
@@ -1,139 +0,0 @@
1
- import { GlossaristModel } from '../models/base.js';
2
- import { TextDiff } from './text-diff.js';
3
-
4
- export const CHANGE_ADDED = 'added';
5
- export const CHANGE_REMOVED = 'removed';
6
- export const CHANGE_CHANGED = 'changed';
7
- export const CHANGE_MATCHED = 'matched';
8
-
9
- function serializeValue(v) {
10
- if (v == null) return null;
11
- if (v instanceof GlossaristModel) return v.toJSON();
12
- if (Array.isArray(v)) return v.map(serializeValue);
13
- if (typeof v === 'object') {
14
- const out = {};
15
- for (const [k, val] of Object.entries(v)) out[k] = serializeValue(val);
16
- return out;
17
- }
18
- return v;
19
- }
20
-
21
- export class Change extends GlossaristModel {
22
- constructor(data = {}) {
23
- super();
24
- this.path = data.path ?? null;
25
- }
26
-
27
- get type() {
28
- throw new Error(`${this.constructor.name} must override type getter`);
29
- }
30
-
31
- static fromJSON(data) {
32
- return deserializeChange(data);
33
- }
34
- }
35
-
36
- export class Added extends Change {
37
- constructor(data = {}) {
38
- super(data);
39
- this.value = data.value ?? null;
40
- }
41
-
42
- get type() { return CHANGE_ADDED; }
43
-
44
- toJSON() {
45
- const obj = { type: CHANGE_ADDED };
46
- if (this.path != null) obj.path = this.path;
47
- obj.value = serializeValue(this.value);
48
- return obj;
49
- }
50
-
51
- static fromJSON(data) {
52
- return new Added(data);
53
- }
54
- }
55
-
56
- export class Removed extends Change {
57
- constructor(data = {}) {
58
- super(data);
59
- this.value = data.value ?? null;
60
- }
61
-
62
- get type() { return CHANGE_REMOVED; }
63
-
64
- toJSON() {
65
- const obj = { type: CHANGE_REMOVED };
66
- if (this.path != null) obj.path = this.path;
67
- obj.value = serializeValue(this.value);
68
- return obj;
69
- }
70
-
71
- static fromJSON(data) {
72
- return new Removed(data);
73
- }
74
- }
75
-
76
- export class Changed extends Change {
77
- constructor(data = {}) {
78
- super(data);
79
- this.oldValue = data.oldValue ?? data.old_value ?? null;
80
- this.newValue = data.newValue ?? data.new_value ?? null;
81
- this._textDiff = data.textDiff ?? (data.text_diff ? TextDiff.fromJSON(data.text_diff) : null);
82
- }
83
-
84
- get type() { return CHANGE_CHANGED; }
85
-
86
- get textDiff() {
87
- return this._textDiff;
88
- }
89
-
90
- toJSON() {
91
- const obj = { type: CHANGE_CHANGED };
92
- if (this.path != null) obj.path = this.path;
93
- obj.old_value = serializeValue(this.oldValue);
94
- obj.new_value = serializeValue(this.newValue);
95
- if (this._textDiff) obj.text_diff = this._textDiff.toJSON();
96
- return obj;
97
- }
98
-
99
- static fromJSON(data) {
100
- return new Changed(data);
101
- }
102
- }
103
-
104
- // Matched is for set-membership records that exist in both sides of a
105
- // comparison but have no directionality — used by ConceptCollectionDiff
106
- // to track which concepts were paired by id, distinct from those that
107
- // were genuinely Added or Removed. Distinct from Added because the data
108
- // has a different semantic meaning; conflating them caused type drift
109
- // in round 1 (TODO.hyperedges-v2/08).
110
- export class Matched extends Change {
111
- constructor(data = {}) {
112
- super(data);
113
- this.value = data.value ?? null;
114
- }
115
-
116
- get type() { return CHANGE_MATCHED; }
117
-
118
- toJSON() {
119
- const obj = { type: CHANGE_MATCHED };
120
- if (this.path != null) obj.path = this.path;
121
- obj.value = serializeValue(this.value);
122
- return obj;
123
- }
124
-
125
- static fromJSON(data) {
126
- return new Matched(data);
127
- }
128
- }
129
-
130
- export function deserializeChange(data) {
131
- switch (data?.type) {
132
- case CHANGE_ADDED: return Added.fromJSON(data);
133
- case CHANGE_REMOVED: return Removed.fromJSON(data);
134
- case CHANGE_CHANGED: return Changed.fromJSON(data);
135
- case CHANGE_MATCHED: return Matched.fromJSON(data);
136
- default:
137
- throw new Error(`Unknown change type: ${data?.type}`);
138
- }
139
- }
@@ -1,227 +0,0 @@
1
- import { GlossaristModel } from '../models/base.js';
2
- import { Added, Removed, Changed, deserializeChange } from './change.js';
3
- import { diffText } from './text-diff.js';
4
-
5
- export class ListDiff extends GlossaristModel {
6
- constructor(data = {}) {
7
- super();
8
- this._added = (data.added ?? []).map(c => c instanceof Added ? c : Added.fromJSON(c));
9
- this._removed = (data.removed ?? []).map(c => c instanceof Removed ? c : Removed.fromJSON(c));
10
- this._changed = (data.changed ?? []).map(c => {
11
- if (c instanceof Changed) return c;
12
- return deserializeChange(c);
13
- });
14
- }
15
-
16
- get added() { return this._added; }
17
- get removed() { return this._removed; }
18
- get changed() { return this._changed; }
19
-
20
- get hasChanges() {
21
- return this._added.length > 0 || this._removed.length > 0 || this._changed.length > 0;
22
- }
23
-
24
- get count() {
25
- return this._added.length + this._removed.length + this._changed.length;
26
- }
27
-
28
- *entries() {
29
- yield* this._added;
30
- yield* this._removed;
31
- yield* this._changed;
32
- }
33
-
34
- toJSON() {
35
- return {
36
- added: this._added.map(c => c.toJSON()),
37
- removed: this._removed.map(c => c.toJSON()),
38
- changed: this._changed.map(c => c.toJSON()),
39
- };
40
- }
41
-
42
- static fromJSON(data) {
43
- return new ListDiff(data);
44
- }
45
- }
46
-
47
- /**
48
- * Diff two ordered lists using LCS alignment.
49
- *
50
- * - Items present in both (by identityKey) at the same relative position
51
- * are "unchanged" and omitted from the result.
52
- * - Items only in the old list are "removed".
53
- * - Items only in the new list are "added".
54
- * - When an adjacent removed+added pair is detected at the same edit
55
- * position, they are merged into a single "changed" entry. If both
56
- * items expose text via textKey, a word-level TextDiff is attached.
57
- *
58
- * @param {Array} oldList
59
- * @param {Array} newList
60
- * @param {Object} [options]
61
- * @param {(item: any) => string} [options.identityKey] - equality key for matching items across lists
62
- * @param {(item: any) => string|null} [options.textKey] - extracts text content for word-level diff on changed items
63
- * @returns {ListDiff}
64
- */
65
- export function diffList(oldList = [], newList = [], options = {}) {
66
- const identityKey = options.identityKey ?? defaultIdentityKey;
67
- const textKey = options.textKey ?? null;
68
-
69
- const oldArr = [...oldList];
70
- const newArr = [...newList];
71
-
72
- const oldKeys = oldArr.map(identityKey);
73
- const newKeys = newArr.map(identityKey);
74
-
75
- const dp = lcsTable(oldKeys, newKeys);
76
- const ops = backtrackOps(dp, oldKeys, newKeys);
77
-
78
- const added = [];
79
- const removed = [];
80
- const changed = [];
81
-
82
- for (let k = 0; k < ops.length; k++) {
83
- const op = ops[k];
84
- if (op.type === 'equal') continue;
85
-
86
- if (op.type === 'remove' && k + 1 < ops.length && ops[k + 1].type === 'add') {
87
- const next = ops[k + 1];
88
- const oldItem = oldArr[op.i];
89
- const newItem = newArr[next.j];
90
- changed.push(buildChanged(oldItem, newItem, textKey));
91
- k++;
92
- } else if (op.type === 'add' && k + 1 < ops.length && ops[k + 1].type === 'remove') {
93
- const next = ops[k + 1];
94
- const oldItem = oldArr[next.i];
95
- const newItem = newArr[op.j];
96
- changed.push(buildChanged(oldItem, newItem, textKey));
97
- k++;
98
- } else if (op.type === 'remove') {
99
- removed.push(new Removed({ value: oldArr[op.i] }));
100
- } else if (op.type === 'add') {
101
- added.push(new Added({ value: newArr[op.j] }));
102
- }
103
- }
104
-
105
- return new ListDiff({ added, removed, changed });
106
- }
107
-
108
- /**
109
- * Diff two unordered collections by identity key (set-based).
110
- *
111
- * - Items matched by identityKey in both lists are compared for full
112
- * equality; if they differ → "changed".
113
- * - Items only in the old list are "removed".
114
- * - Items only in the new list are "added".
115
- *
116
- * Use this for collections where order does not carry meaning (e.g.
117
- * designations matched by term text, sources matched by ref, dates
118
- * matched by type).
119
- *
120
- * @param {Array} oldList
121
- * @param {Array} newList
122
- * @param {Object} options
123
- * @param {(item: any) => string} options.identityKey - key that identifies the same item across lists
124
- * @param {(item: any) => string|null} [options.textKey] - extracts text for word-level diff on changed items
125
- * @returns {ListDiff}
126
- */
127
- export function diffSet(oldList = [], newList = [], options = {}) {
128
- const identityKey = options.identityKey ?? defaultIdentityKey;
129
- const textKey = options.textKey ?? null;
130
-
131
- const oldMap = new Map();
132
- for (const item of oldList) {
133
- oldMap.set(identityKey(item), item);
134
- }
135
-
136
- const seenKeys = new Set();
137
- const added = [];
138
- const changed = [];
139
-
140
- for (const item of newList) {
141
- const key = identityKey(item);
142
- seenKeys.add(key);
143
- if (oldMap.has(key)) {
144
- const oldItem = oldMap.get(key);
145
- if (!itemsEqual(oldItem, item)) {
146
- changed.push(buildChanged(oldItem, item, textKey));
147
- }
148
- } else {
149
- added.push(new Added({ value: item }));
150
- }
151
- }
152
-
153
- const removed = [];
154
- for (const item of oldList) {
155
- if (!seenKeys.has(identityKey(item))) {
156
- removed.push(new Removed({ value: item }));
157
- }
158
- }
159
-
160
- return new ListDiff({ added, removed, changed });
161
- }
162
-
163
- function buildChanged(oldItem, newItem, textKey) {
164
- const opts = { oldValue: oldItem, newValue: newItem };
165
- if (textKey) {
166
- const oldText = textKey(oldItem);
167
- const newText = textKey(newItem);
168
- if (oldText != null || newText != null) {
169
- opts.textDiff = diffText(oldText ?? '', newText ?? '');
170
- }
171
- }
172
- return new Changed(opts);
173
- }
174
-
175
- function itemsEqual(a, b) {
176
- if (a === b) return true;
177
- if (a instanceof GlossaristModel && b instanceof GlossaristModel) {
178
- return a.equals(b);
179
- }
180
- return JSON.stringify(a) === JSON.stringify(b);
181
- }
182
-
183
- function defaultIdentityKey(item) {
184
- if (item == null) return '';
185
- if (typeof item === 'string') return item;
186
- if (typeof item === 'number') return String(item);
187
- if (item instanceof GlossaristModel) {
188
- return JSON.stringify(item.toJSON());
189
- }
190
- return JSON.stringify(item);
191
- }
192
-
193
- function lcsTable(aKeys, bKeys) {
194
- const m = aKeys.length;
195
- const n = bKeys.length;
196
- const dp = Array.from({ length: m + 1 }, () => new Uint32Array(n + 1));
197
- for (let i = 1; i <= m; i++) {
198
- for (let j = 1; j <= n; j++) {
199
- dp[i][j] = aKeys[i - 1] === bKeys[j - 1]
200
- ? dp[i - 1][j - 1] + 1
201
- : Math.max(dp[i - 1][j], dp[i][j - 1]);
202
- }
203
- }
204
- return dp;
205
- }
206
-
207
- function backtrackOps(dp, aKeys, bKeys) {
208
- const ops = [];
209
- let i = aKeys.length;
210
- let j = bKeys.length;
211
- while (i > 0 && j > 0) {
212
- if (aKeys[i - 1] === bKeys[j - 1]) {
213
- ops.push({ type: 'equal', i: i - 1, j: j - 1 });
214
- i--; j--;
215
- } else if (dp[i - 1][j] >= dp[i][j - 1]) {
216
- ops.push({ type: 'remove', i: i - 1, j });
217
- i--;
218
- } else {
219
- ops.push({ type: 'add', i, j: j - 1 });
220
- j--;
221
- }
222
- }
223
- while (i > 0) { ops.push({ type: 'remove', i: i - 1, j }); i--; }
224
- while (j > 0) { ops.push({ type: 'add', i, j: j - 1 }); j--; }
225
- ops.reverse();
226
- return ops;
227
- }
@@ -1 +0,0 @@
1
- export { ConceptToGlossTransform } from './concept-to-gloss.transform.js';
File without changes