pro-editor-schema 0.2.0 → 0.2.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/README.md CHANGED
@@ -7,41 +7,64 @@ TypeScript library that defines XML/HTML schema rules used by the editor and pro
7
7
  - Schema definitions for supported tags and schema codes.
8
8
  - Shared schema constants and attribute helpers.
9
9
  - Runtime schema lookup utilities.
10
- - XML validation utilities (`validateXml`, attribute/child validators, footnote and link validators).
10
+ - XML validation utilities (`validateXml` and related validators).
11
11
  - Type-safe element factory (`createElement`) based on schema definitions.
12
12
 
13
- ## Install and build
13
+ ## Install
14
14
 
15
15
  ```bash
16
16
  npm install
17
- npm run typecheck
18
- npm run test
19
- npm run build
20
17
  ```
21
18
 
19
+ ## Scripts
20
+
21
+ | Command | Description |
22
+ | --- | --- |
23
+ | `npm run dev` | Build in watch mode |
24
+ | `npm run build` | Build CJS, ESM, and type declarations |
25
+ | `npm run typecheck` | Run TypeScript without emitting files |
26
+ | `npm run lint` | Run ESLint on `src/` |
27
+ | `npm run test` | Run tests once with Vitest |
28
+ | `npm run test:watch` | Run tests in watch mode |
29
+ | `npm run check` | Run the full verification pipeline (`npm ci`, `typecheck`, `lint`, `test`, `build`) |
30
+
31
+ For local development, `npm run check` is the fastest way to verify everything before pushing.
32
+
33
+ ## CI
34
+
35
+ GitHub Actions runs on push and pull requests to `main` / `master`:
36
+
37
+ - **Typecheck** — `npm run typecheck`
38
+ - **Lint** — `npm run lint`
39
+ - **Test** — `npm test`
40
+
41
+ Workflow file: `.github/workflows/ci.yml`
42
+
22
43
  ## Project structure
23
44
 
24
- - `src/index.ts`: public exports for schemas, types, shared constants, and validation.
25
- - `src/schema/schema.types.ts`: core schema and validation-related types.
26
- - `src/schema/schemas.ts`: central `SCHEMAS` map (`code -> schema`).
27
- - `src/schema/elementSchemas/elements`: element schema definitions and `ELEMENT_SCHEMAS`.
28
- - `src/schema/elementSchemas/atoms`: atom schema definitions and `ATOM_SCHEMAS`.
29
- - `src/schema/elementSchemas/inlineElements`: inline schema definitions and `INLINE_ELEMENT_SCHEMAS`.
30
- - `src/schema/shared`: shared attribute and ignored-node constants.
31
- - `src/schema/utils`: schema lookup and element creation helpers.
32
- - `src/validation`: document validation entrypoint and validation modules.
45
+ - `src/index.ts` — public exports for schemas, types, shared constants, and validation.
46
+ - `src/schema/schema.types.ts` — core schema and validation-related types.
47
+ - `src/schema/schemas.ts` — central `SCHEMAS` map (`code -> schema`).
48
+ - `src/schema/elementSchemas/elements` — element schema definitions and `ELEMENT_SCHEMAS`.
49
+ - `src/schema/elementSchemas/atoms` — atom schema definitions and `ATOM_SCHEMAS`.
50
+ - `src/schema/elementSchemas/inlineElements` — inline schema definitions and `INLINE_ELEMENT_SCHEMAS`.
51
+ - `src/schema/shared` — shared attribute and ignored-node constants.
52
+ - `src/schema/utils` — schema lookup, element creation, and label helpers.
53
+ - `src/validation` — document validation entrypoint and validation modules.
54
+ - `src/**/*.test.ts` — Vitest test files (run with `happy-dom`).
33
55
 
34
56
  ## Schema model
35
57
 
36
58
  Each schema defines:
37
59
 
38
- - `code`: internal schema identifier.
39
- - `kind`: schema category (`element`, `atom`, `inline`).
40
- - `tag`: uppercase XML tag name used in documents.
41
- - `requiredAttributes`: required attributes (free or restricted values).
42
- - `optionalAttributes`: optional attributes (free or restricted values).
43
- - `defaultChildCode`: preferred child tag (optional, used by some block-like schemas).
44
- - `allowedChildrenCodes`: allowed child tags and whether each child is required.
60
+ - `code` — internal schema identifier.
61
+ - `kind` — schema category (`element`, `atom`, `inline`).
62
+ - `tag` — uppercase XML tag name used in documents.
63
+ - `requiredAttributes` — required attributes (free or restricted values).
64
+ - `optionalAttributes` — optional attributes (free or restricted values).
65
+ - `defaultChildCode` — preferred child tag (optional, used by some block-like schemas).
66
+ - `allowedChildrenCodes` — allowed child tags and whether each child is required.
67
+ - `matchesElement` — optional predicate to resolve elements that share the same tag (for example `SPAN`, `SECTION1`).
45
68
 
46
69
  Schemas are combined in `SCHEMAS` from:
47
70
 
@@ -49,15 +72,29 @@ Schemas are combined in `SCHEMAS` from:
49
72
  - `ATOM_SCHEMAS`
50
73
  - `INLINE_ELEMENT_SCHEMAS`
51
74
 
75
+ ### Resolving schemas from DOM elements
76
+
77
+ `findSchemaByElement` matches by tag name. When multiple schemas share a tag, the first schema with a matching `matchesElement` predicate wins.
78
+
79
+ Examples:
80
+
81
+ - `SPAN` with class `ins` → `insertion`
82
+ - `SPAN` with class `del` → `deletion`
83
+ - `SPAN` with class `cmtQurySection` → `commentQuerySection`
84
+ - `SECTION1` with `role="H1"` → `heading`
85
+ - `SECTION1` with `role="ch"` or a `FOOTNOTE` child → `footnoteSection`
86
+
87
+ When adding a new schema that reuses an existing tag, define `matchesElement` on every schema that shares that tag.
88
+
52
89
  ## Public API highlights
53
90
 
54
91
  From `src/index.ts`, consumers can import:
55
92
 
56
- - Types: `Schema`, `SchemaCode`, `ElementSchemaCode`, `AtomSchemaCode`, `InlineSchemaCode`
57
- - Maps: `SCHEMAS`, `ELEMENT_SCHEMAS`, `ATOM_SCHEMAS`, `INLINE_ELEMENT_SCHEMAS`
58
- - Utilities: `findSchemaByCode`, `findSchemaByElement`, `createElement`
59
- - Validators: `validateXml`, `validateFootnotes`, `validateLinkend`, `validateAttributes`, `validateChildren`
60
- - Shared constants: ignored elements/attributes and global attribute constants
93
+ - **Types:** `Schema`, `SchemaCode`, `ElementSchemaCode`, `AtomSchemaCode`, `InlineSchemaCode`, `ValidationError`
94
+ - **Maps:** `SCHEMAS`, `ELEMENT_SCHEMAS`, `ATOM_SCHEMAS`, `INLINE_ELEMENT_SCHEMAS`
95
+ - **Utilities:** `findSchemaByCode`, `findSchemaByElement`, `createElement`, `ElementLabel`
96
+ - **Validation:** `validateXml`
97
+ - **Shared constants:** ignored elements/attributes, global attribute constants, dependents forbidden elements
61
98
 
62
99
  ## Usage examples
63
100
 
@@ -79,7 +116,6 @@ const para = createElement({
79
116
  attributes: {
80
117
  store: "body",
81
118
  "ele-id": "p-1",
82
- role: "body",
83
119
  },
84
120
  });
85
121
  ```
@@ -89,23 +125,41 @@ const para = createElement({
89
125
  ```ts
90
126
  import { validateXml } from "pro-editor-schema";
91
127
 
92
- const errors = validateXml(rootElement, linkedIds, true);
128
+ const errors = validateXml(
129
+ rootElement,
130
+ elementsToLink,
131
+ linkElements,
132
+ ["para", "heading"],
133
+ );
93
134
  ```
94
135
 
95
- `validateXml` performs:
136
+ `validateXml` parameters:
137
+
138
+ - `xml` — root element to validate.
139
+ - `elementsToLink` — IDs available as `linkend` targets.
140
+ - `linkElements` — IDs cited by links (used for reference validation).
141
+ - `schemasToValidateAgainst` — schema codes to run attribute/child validation against.
142
+
143
+ `validateXml` always runs:
144
+
145
+ - Consecutive footnote number checks
146
+ - Consecutive footnote cue checks
147
+
148
+ It also runs per-element checks for:
149
+
150
+ - Link targets (`linkend`) on linkable elements
151
+ - Uncited bibliographic references
152
+ - Unreferenced footnotes
96
153
 
97
- - Footnote sequence checks
98
- - Footnote cue sequence checks
99
- - Link target checks (`linkend`)
100
- - Optional schema-based attribute and child validation when enabled
154
+ Schema-based attribute and child validation only runs for codes listed in `schemasToValidateAgainst`.
101
155
 
102
156
  ## How validation works
103
157
 
104
158
  - `validateXml` traverses the XML tree and delegates checks per element.
105
- - `findSchemaByElement` resolves schema from classes, role, and tag fallback.
106
- - `validateAttributes` ensures required attributes exist and restricted values are valid.
159
+ - `findSchemaByElement` resolves schema from tag and optional `matchesElement`.
160
+ - `validateAttributes` validates restricted attribute values.
107
161
  - `validateChildren` ensures child tags are allowed and required children exist.
108
- - `validateFootnotes` and `validateLinkend` provide cross-reference integrity checks.
162
+ - `validateFootnotes`, `validateLinkend`, and `validateReferenceCited` provide cross-reference integrity checks.
109
163
 
110
164
  Validation results use `ValidationError`:
111
165
 
@@ -118,23 +172,23 @@ Validation results use `ValidationError`:
118
172
 
119
173
  ## Adding or updating a schema
120
174
 
121
- 1. Create or update schema file in one of:
175
+ 1. Create or update a schema file in one of:
122
176
  - `src/schema/elementSchemas/elements`
123
177
  - `src/schema/elementSchemas/atoms`
124
178
  - `src/schema/elementSchemas/inlineElements`
125
- 2. Define schema with `createSchema(...)`.
126
- 3. Add it to the corresponding registry:
179
+ 2. Define the schema with `createSchema(...)`.
180
+ 3. If the tag is already used by another schema, add a `matchesElement` predicate.
181
+ 4. Register it in the corresponding map:
127
182
  - `ELEMENT_SCHEMAS`
128
183
  - `ATOM_SCHEMAS`
129
184
  - `INLINE_ELEMENT_SCHEMAS`
130
- 4. Ensure the schema code exists in the matching union type in `schema.types.ts`.
131
- 5. If needed, export constants/types from folder `index.ts` and `src/index.ts`.
132
- 6. Run:
133
- - `npm run typecheck`
134
- - `npm run test`
185
+ 5. Ensure the schema code exists in the matching union type in `schema.types.ts`.
186
+ 6. Add or update tests for any new validation or utility behavior.
187
+ 7. Run `npm run check`.
135
188
 
136
189
  ## Notes
137
190
 
138
191
  - Tag matching is done in uppercase in schema definitions and validation.
139
192
  - Some schema codes represent block/group-like structures while still being typed as `kind: "element"` in the current model.
140
193
  - Ignored attributes and ignored UI helper elements are excluded from strict validation by design.
194
+ - Track-changes atoms (`insertion`, `deletion`) are unwrapped when validating required children.
package/dist/index.d.mts CHANGED
@@ -1,24 +1,23 @@
1
1
  type SchemaKind = "element" | "atom" | "inline";
2
2
  type FreeAttribute = string;
3
- type RestrictedAttribute<T = readonly string[]> = {
3
+ type RestrictedAttribute = {
4
4
  name: string;
5
- values: T;
5
+ values: string[];
6
6
  defaultValue: string;
7
7
  };
8
- type AttributeDefinition<T = readonly string[]> = FreeAttribute | RestrictedAttribute<T>;
8
+ type AttributeDefinition = FreeAttribute | RestrictedAttribute;
9
9
  type AllowedChildren = {
10
10
  code: SchemaCode;
11
11
  required: boolean;
12
12
  };
13
- type Schema<TRequiredAttributes = readonly AttributeDefinition[], TOptionalAttributes = readonly AttributeDefinition[]> = {
13
+ type Schema = {
14
14
  code: SchemaCode;
15
15
  kind: SchemaKind;
16
16
  tag: string;
17
- requiredAttributes: TRequiredAttributes;
18
- optionalAttributes: TOptionalAttributes;
17
+ requiredAttributes: AttributeDefinition[];
18
+ optionalAttributes: AttributeDefinition[];
19
19
  defaultChildCode?: SchemaCode;
20
20
  allowedChildrenCodes: AllowedChildren[];
21
- matchesElement?: (element: Element) => boolean;
22
21
  };
23
22
  type SchemaCode = ElementSchemaCode | InlineSchemaCode | AtomSchemaCode;
24
23
  type ElementSchemaCode = "referenceBibliomixed" | "referenceBibliography" | "bibliodiv" | "degrees" | "email" | "equation" | "fignote" | "figsource" | "firstname" | "footnote" | "footnoteno" | "honorific" | "img" | "informalfigure" | "keyword" | "label1" | "li" | "line" | "lrh" | "math" | "mathphrase" | "merror" | "mfrac" | "mi" | "mmultiscripts" | "mn" | "mo" | "mover" | "mpadded" | "mphantom" | "mprescripts" | "mroot" | "mrow" | "ms" | "mspace" | "msqrt" | "mstyle" | "msub" | "msubsup" | "msup" | "mtable" | "mtd" | "mtext" | "mtr" | "munder" | "munderover" | "olink" | "org" | "orgname" | "para" | "punc" | "rrh" | "source1" | "speaker" | "subtitle1" | "surname" | "tag" | "tblfn" | "tblsource" | "thead" | "title1" | "token" | "tp" | "abstract1" | "affiliation" | "alttext" | "appendix" | "author" | "authorgroup" | "blockquote" | "caption1" | "chapter" | "dialogue" | "epigraph" | "figure1" | "footnoteSection" | "heading" | "imageobject" | "info1" | "keywordset" | "mediaobject" | "ol" | "personname" | "poem" | "sidebar" | "table" | "table1" | "tbody" | "td" | "tgroup" | "tr" | "ul";
@@ -31,25 +30,18 @@ declare const isTrackChangesSchema: (schema: Schema) => boolean;
31
30
  declare const isIgnoredAttribute: (attribute: string) => boolean;
32
31
  declare const isIgnoredElement: (element: Element) => boolean;
33
32
  declare const isIgnoredValidation: (code: SchemaCode) => boolean;
34
- declare const findSchemaByCode: (code: SchemaCode) => Schema;
33
+ declare const findSchemaByCode: (code: SchemaCode) => Schema | undefined;
34
+ declare const findSchemaByTagName: (tagName: string) => Schema[];
35
35
  declare const findSchemaByElement: (element: Element) => Schema | undefined;
36
36
  declare const isAttributeRestricted: (attr: AttributeDefinition) => attr is RestrictedAttribute;
37
37
  declare const getAttributeName: (attribute: AttributeDefinition) => string;
38
- declare const findAttributeDefinitionByName: (definitions: readonly AttributeDefinition[], name: string) => AttributeDefinition | undefined;
38
+ declare const findAttributeDefinitionByName: (definitions: AttributeDefinition[], name: string) => AttributeDefinition | undefined;
39
39
 
40
- type AttributeValue<TAttribute extends AttributeDefinition> = TAttribute extends string ? string : TAttribute extends RestrictedAttribute<infer TValues> ? TValues extends readonly string[] ? TValues[number] : string : never;
41
- type SchemaAttributes<TSchema extends Schema> = TSchema extends Schema<infer TRequired extends readonly AttributeDefinition[], infer TOptional extends readonly AttributeDefinition[]> ? {
42
- [TAttribute in TRequired[number] as TAttribute extends string ? TAttribute : TAttribute extends RestrictedAttribute ? TAttribute["name"] : never]: AttributeValue<TAttribute>;
43
- } & {
44
- [TAttribute in TOptional[number] as TAttribute extends string ? TAttribute : TAttribute extends RestrictedAttribute ? TAttribute["name"] : never]?: AttributeValue<TAttribute>;
45
- } : never;
46
- type CreateElementParams<TSchema extends Schema> = {
40
+ type CreateElementParams = {
47
41
  schemaCode: SchemaCode;
48
- attributes: SchemaAttributes<TSchema>;
42
+ attributes: Record<string, string>;
49
43
  };
50
- declare const createElement: <TSchema extends Schema>({ schemaCode, attributes, }: CreateElementParams<TSchema>) => HTMLElement;
51
-
52
- declare const ElementLabel: Record<SchemaCode, string>;
44
+ declare const createElement: ({ schemaCode, attributes, }: CreateElementParams) => HTMLElement;
53
45
 
54
46
  declare const IGNORED_ELEMENT_SELECTORS: string[];
55
47
  declare const IGNORED_VALIDATION_ELEMENT_SELECTORS: SchemaCode[];
@@ -76,15 +68,15 @@ declare const FIGURE_PLACEMENT_ATTRIBUTE = "data-figure-placement";
76
68
  declare const LAYOUT_CELL_WIDTH_ATTRIBUTE = "data-cell-width";
77
69
  declare const LAYOUT_TABLE_ORIENTATION_ATTRIBUTE = "data-table-orientation";
78
70
  declare const LAYOUT_TABLE_PLACEMENT_ATTRIBUTE = "data-table-placement";
79
- declare const LAYOUT_ATTRIBUTES: readonly ["data-alignment", "data-left-indent", "data-right-indent", "data-special-indent", "data-space-above", "data-space-below", "data-indent-type", "data-layout-user", "data-paragraph-style"];
71
+ declare const LAYOUT_ATTRIBUTES: string[];
80
72
  declare const TRACK_CHANGES_ATTR_DATA_USERNAME = "data-username";
81
73
  declare const TRACK_CHANGES_ATTR_DATA_USERID = "data-userid";
82
74
  declare const TRACK_CHANGES_ATTR_DATA_TIME = "data-time";
83
75
  declare const TRACK_CHANGES_ATTR_DATA_DATE = "data-date";
84
- declare const TRACK_CHANGES_ATTRS: readonly ["data-username", "data-userid", "data-time", "data-date"];
76
+ declare const TRACK_CHANGES_ATTRS: string[];
85
77
  declare const ELE_ID_ATTRIBUTE = "ele-id";
86
78
  declare const STORE_ATTRIBUTE = "store";
87
- declare const ID_ATTRS: readonly ["store", "ele-id"];
79
+ declare const ID_ATTRS: string[];
88
80
 
89
81
  declare const ELEMENT_SCHEMAS: Record<ElementSchemaCode, Schema>;
90
82
 
@@ -115,4 +107,4 @@ type ValidationError = {
115
107
  */
116
108
  declare const validateXml: (xml: Element, elementsToLink: string[], linkElements: string[], schemasToValidateAgainst: SchemaCode[]) => ValidationError[];
117
109
 
118
- export { ALIGNMENT_ATTRIBUTE, ATOM_SCHEMAS, type AllowedChildren, type AtomSchemaCode, type AttributeDefinition, DEPENDENT_FORBIDDEN_ELEMENTS, ELEMENT_SCHEMAS, ELE_ID_ATTRIBUTE, ElementLabel, type ElementSchemaCode, FIGURE_PLACEMENT_ATTRIBUTE, type FreeAttribute, ID_ATTRS, IGNORED_ATTRIBUTES, IGNORED_ATTRIBUTES_STARTS_WITH, IGNORED_ELEMENT_SELECTORS, IGNORED_VALIDATION_ELEMENT_SELECTORS, INDENT_TYPE_ATTRIBUTE, INLINE_ELEMENT_SCHEMAS, type InlineSchemaCode, LAYOUT_ATTRIBUTES, LAYOUT_CELL_WIDTH_ATTRIBUTE, LAYOUT_TABLE_ORIENTATION_ATTRIBUTE, LAYOUT_TABLE_PLACEMENT_ATTRIBUTE, LAYOUT_USER_ATTRIBUTE, LEFT_INDENT_ATTRIBUTE, RIGHT_INDENT_ATTRIBUTE, type RestrictedAttribute, SCHEMAS, SPACE_ABOVE_ATTRIBUTE, SPACE_BELOW_ATTRIBUTE, SPECIAL_INDENT_ATTRIBUTE, STORE_ATTRIBUTE, STYLE_NAME_ATTRIBUTE, type Schema, type SchemaCode, type SchemaKind, TRACK_CHANGES_ATTRS, TRACK_CHANGES_ATTR_DATA_DATE, TRACK_CHANGES_ATTR_DATA_TIME, TRACK_CHANGES_ATTR_DATA_USERID, TRACK_CHANGES_ATTR_DATA_USERNAME, type ValidationError, type ValidationErrorKind, type ValidationErrorLevel, createElement, findAttributeDefinitionByName, findSchemaByCode, findSchemaByElement, getAttributeName, isAttributeRestricted, isIgnoredAttribute, isIgnoredElement, isIgnoredValidation, isTrackChangesSchema, validateXml };
110
+ export { ALIGNMENT_ATTRIBUTE, ATOM_SCHEMAS, type AllowedChildren, type AtomSchemaCode, type AttributeDefinition, DEPENDENT_FORBIDDEN_ELEMENTS, ELEMENT_SCHEMAS, ELE_ID_ATTRIBUTE, type ElementSchemaCode, FIGURE_PLACEMENT_ATTRIBUTE, type FreeAttribute, ID_ATTRS, IGNORED_ATTRIBUTES, IGNORED_ATTRIBUTES_STARTS_WITH, IGNORED_ELEMENT_SELECTORS, IGNORED_VALIDATION_ELEMENT_SELECTORS, INDENT_TYPE_ATTRIBUTE, INLINE_ELEMENT_SCHEMAS, type InlineSchemaCode, LAYOUT_ATTRIBUTES, LAYOUT_CELL_WIDTH_ATTRIBUTE, LAYOUT_TABLE_ORIENTATION_ATTRIBUTE, LAYOUT_TABLE_PLACEMENT_ATTRIBUTE, LAYOUT_USER_ATTRIBUTE, LEFT_INDENT_ATTRIBUTE, RIGHT_INDENT_ATTRIBUTE, type RestrictedAttribute, SCHEMAS, SPACE_ABOVE_ATTRIBUTE, SPACE_BELOW_ATTRIBUTE, SPECIAL_INDENT_ATTRIBUTE, STORE_ATTRIBUTE, STYLE_NAME_ATTRIBUTE, type Schema, type SchemaCode, type SchemaKind, TRACK_CHANGES_ATTRS, TRACK_CHANGES_ATTR_DATA_DATE, TRACK_CHANGES_ATTR_DATA_TIME, TRACK_CHANGES_ATTR_DATA_USERID, TRACK_CHANGES_ATTR_DATA_USERNAME, type ValidationError, type ValidationErrorKind, type ValidationErrorLevel, createElement, findAttributeDefinitionByName, findSchemaByCode, findSchemaByElement, findSchemaByTagName, getAttributeName, isAttributeRestricted, isIgnoredAttribute, isIgnoredElement, isIgnoredValidation, isTrackChangesSchema, validateXml };
package/dist/index.d.ts CHANGED
@@ -1,24 +1,23 @@
1
1
  type SchemaKind = "element" | "atom" | "inline";
2
2
  type FreeAttribute = string;
3
- type RestrictedAttribute<T = readonly string[]> = {
3
+ type RestrictedAttribute = {
4
4
  name: string;
5
- values: T;
5
+ values: string[];
6
6
  defaultValue: string;
7
7
  };
8
- type AttributeDefinition<T = readonly string[]> = FreeAttribute | RestrictedAttribute<T>;
8
+ type AttributeDefinition = FreeAttribute | RestrictedAttribute;
9
9
  type AllowedChildren = {
10
10
  code: SchemaCode;
11
11
  required: boolean;
12
12
  };
13
- type Schema<TRequiredAttributes = readonly AttributeDefinition[], TOptionalAttributes = readonly AttributeDefinition[]> = {
13
+ type Schema = {
14
14
  code: SchemaCode;
15
15
  kind: SchemaKind;
16
16
  tag: string;
17
- requiredAttributes: TRequiredAttributes;
18
- optionalAttributes: TOptionalAttributes;
17
+ requiredAttributes: AttributeDefinition[];
18
+ optionalAttributes: AttributeDefinition[];
19
19
  defaultChildCode?: SchemaCode;
20
20
  allowedChildrenCodes: AllowedChildren[];
21
- matchesElement?: (element: Element) => boolean;
22
21
  };
23
22
  type SchemaCode = ElementSchemaCode | InlineSchemaCode | AtomSchemaCode;
24
23
  type ElementSchemaCode = "referenceBibliomixed" | "referenceBibliography" | "bibliodiv" | "degrees" | "email" | "equation" | "fignote" | "figsource" | "firstname" | "footnote" | "footnoteno" | "honorific" | "img" | "informalfigure" | "keyword" | "label1" | "li" | "line" | "lrh" | "math" | "mathphrase" | "merror" | "mfrac" | "mi" | "mmultiscripts" | "mn" | "mo" | "mover" | "mpadded" | "mphantom" | "mprescripts" | "mroot" | "mrow" | "ms" | "mspace" | "msqrt" | "mstyle" | "msub" | "msubsup" | "msup" | "mtable" | "mtd" | "mtext" | "mtr" | "munder" | "munderover" | "olink" | "org" | "orgname" | "para" | "punc" | "rrh" | "source1" | "speaker" | "subtitle1" | "surname" | "tag" | "tblfn" | "tblsource" | "thead" | "title1" | "token" | "tp" | "abstract1" | "affiliation" | "alttext" | "appendix" | "author" | "authorgroup" | "blockquote" | "caption1" | "chapter" | "dialogue" | "epigraph" | "figure1" | "footnoteSection" | "heading" | "imageobject" | "info1" | "keywordset" | "mediaobject" | "ol" | "personname" | "poem" | "sidebar" | "table" | "table1" | "tbody" | "td" | "tgroup" | "tr" | "ul";
@@ -31,25 +30,18 @@ declare const isTrackChangesSchema: (schema: Schema) => boolean;
31
30
  declare const isIgnoredAttribute: (attribute: string) => boolean;
32
31
  declare const isIgnoredElement: (element: Element) => boolean;
33
32
  declare const isIgnoredValidation: (code: SchemaCode) => boolean;
34
- declare const findSchemaByCode: (code: SchemaCode) => Schema;
33
+ declare const findSchemaByCode: (code: SchemaCode) => Schema | undefined;
34
+ declare const findSchemaByTagName: (tagName: string) => Schema[];
35
35
  declare const findSchemaByElement: (element: Element) => Schema | undefined;
36
36
  declare const isAttributeRestricted: (attr: AttributeDefinition) => attr is RestrictedAttribute;
37
37
  declare const getAttributeName: (attribute: AttributeDefinition) => string;
38
- declare const findAttributeDefinitionByName: (definitions: readonly AttributeDefinition[], name: string) => AttributeDefinition | undefined;
38
+ declare const findAttributeDefinitionByName: (definitions: AttributeDefinition[], name: string) => AttributeDefinition | undefined;
39
39
 
40
- type AttributeValue<TAttribute extends AttributeDefinition> = TAttribute extends string ? string : TAttribute extends RestrictedAttribute<infer TValues> ? TValues extends readonly string[] ? TValues[number] : string : never;
41
- type SchemaAttributes<TSchema extends Schema> = TSchema extends Schema<infer TRequired extends readonly AttributeDefinition[], infer TOptional extends readonly AttributeDefinition[]> ? {
42
- [TAttribute in TRequired[number] as TAttribute extends string ? TAttribute : TAttribute extends RestrictedAttribute ? TAttribute["name"] : never]: AttributeValue<TAttribute>;
43
- } & {
44
- [TAttribute in TOptional[number] as TAttribute extends string ? TAttribute : TAttribute extends RestrictedAttribute ? TAttribute["name"] : never]?: AttributeValue<TAttribute>;
45
- } : never;
46
- type CreateElementParams<TSchema extends Schema> = {
40
+ type CreateElementParams = {
47
41
  schemaCode: SchemaCode;
48
- attributes: SchemaAttributes<TSchema>;
42
+ attributes: Record<string, string>;
49
43
  };
50
- declare const createElement: <TSchema extends Schema>({ schemaCode, attributes, }: CreateElementParams<TSchema>) => HTMLElement;
51
-
52
- declare const ElementLabel: Record<SchemaCode, string>;
44
+ declare const createElement: ({ schemaCode, attributes, }: CreateElementParams) => HTMLElement;
53
45
 
54
46
  declare const IGNORED_ELEMENT_SELECTORS: string[];
55
47
  declare const IGNORED_VALIDATION_ELEMENT_SELECTORS: SchemaCode[];
@@ -76,15 +68,15 @@ declare const FIGURE_PLACEMENT_ATTRIBUTE = "data-figure-placement";
76
68
  declare const LAYOUT_CELL_WIDTH_ATTRIBUTE = "data-cell-width";
77
69
  declare const LAYOUT_TABLE_ORIENTATION_ATTRIBUTE = "data-table-orientation";
78
70
  declare const LAYOUT_TABLE_PLACEMENT_ATTRIBUTE = "data-table-placement";
79
- declare const LAYOUT_ATTRIBUTES: readonly ["data-alignment", "data-left-indent", "data-right-indent", "data-special-indent", "data-space-above", "data-space-below", "data-indent-type", "data-layout-user", "data-paragraph-style"];
71
+ declare const LAYOUT_ATTRIBUTES: string[];
80
72
  declare const TRACK_CHANGES_ATTR_DATA_USERNAME = "data-username";
81
73
  declare const TRACK_CHANGES_ATTR_DATA_USERID = "data-userid";
82
74
  declare const TRACK_CHANGES_ATTR_DATA_TIME = "data-time";
83
75
  declare const TRACK_CHANGES_ATTR_DATA_DATE = "data-date";
84
- declare const TRACK_CHANGES_ATTRS: readonly ["data-username", "data-userid", "data-time", "data-date"];
76
+ declare const TRACK_CHANGES_ATTRS: string[];
85
77
  declare const ELE_ID_ATTRIBUTE = "ele-id";
86
78
  declare const STORE_ATTRIBUTE = "store";
87
- declare const ID_ATTRS: readonly ["store", "ele-id"];
79
+ declare const ID_ATTRS: string[];
88
80
 
89
81
  declare const ELEMENT_SCHEMAS: Record<ElementSchemaCode, Schema>;
90
82
 
@@ -115,4 +107,4 @@ type ValidationError = {
115
107
  */
116
108
  declare const validateXml: (xml: Element, elementsToLink: string[], linkElements: string[], schemasToValidateAgainst: SchemaCode[]) => ValidationError[];
117
109
 
118
- export { ALIGNMENT_ATTRIBUTE, ATOM_SCHEMAS, type AllowedChildren, type AtomSchemaCode, type AttributeDefinition, DEPENDENT_FORBIDDEN_ELEMENTS, ELEMENT_SCHEMAS, ELE_ID_ATTRIBUTE, ElementLabel, type ElementSchemaCode, FIGURE_PLACEMENT_ATTRIBUTE, type FreeAttribute, ID_ATTRS, IGNORED_ATTRIBUTES, IGNORED_ATTRIBUTES_STARTS_WITH, IGNORED_ELEMENT_SELECTORS, IGNORED_VALIDATION_ELEMENT_SELECTORS, INDENT_TYPE_ATTRIBUTE, INLINE_ELEMENT_SCHEMAS, type InlineSchemaCode, LAYOUT_ATTRIBUTES, LAYOUT_CELL_WIDTH_ATTRIBUTE, LAYOUT_TABLE_ORIENTATION_ATTRIBUTE, LAYOUT_TABLE_PLACEMENT_ATTRIBUTE, LAYOUT_USER_ATTRIBUTE, LEFT_INDENT_ATTRIBUTE, RIGHT_INDENT_ATTRIBUTE, type RestrictedAttribute, SCHEMAS, SPACE_ABOVE_ATTRIBUTE, SPACE_BELOW_ATTRIBUTE, SPECIAL_INDENT_ATTRIBUTE, STORE_ATTRIBUTE, STYLE_NAME_ATTRIBUTE, type Schema, type SchemaCode, type SchemaKind, TRACK_CHANGES_ATTRS, TRACK_CHANGES_ATTR_DATA_DATE, TRACK_CHANGES_ATTR_DATA_TIME, TRACK_CHANGES_ATTR_DATA_USERID, TRACK_CHANGES_ATTR_DATA_USERNAME, type ValidationError, type ValidationErrorKind, type ValidationErrorLevel, createElement, findAttributeDefinitionByName, findSchemaByCode, findSchemaByElement, getAttributeName, isAttributeRestricted, isIgnoredAttribute, isIgnoredElement, isIgnoredValidation, isTrackChangesSchema, validateXml };
110
+ export { ALIGNMENT_ATTRIBUTE, ATOM_SCHEMAS, type AllowedChildren, type AtomSchemaCode, type AttributeDefinition, DEPENDENT_FORBIDDEN_ELEMENTS, ELEMENT_SCHEMAS, ELE_ID_ATTRIBUTE, type ElementSchemaCode, FIGURE_PLACEMENT_ATTRIBUTE, type FreeAttribute, ID_ATTRS, IGNORED_ATTRIBUTES, IGNORED_ATTRIBUTES_STARTS_WITH, IGNORED_ELEMENT_SELECTORS, IGNORED_VALIDATION_ELEMENT_SELECTORS, INDENT_TYPE_ATTRIBUTE, INLINE_ELEMENT_SCHEMAS, type InlineSchemaCode, LAYOUT_ATTRIBUTES, LAYOUT_CELL_WIDTH_ATTRIBUTE, LAYOUT_TABLE_ORIENTATION_ATTRIBUTE, LAYOUT_TABLE_PLACEMENT_ATTRIBUTE, LAYOUT_USER_ATTRIBUTE, LEFT_INDENT_ATTRIBUTE, RIGHT_INDENT_ATTRIBUTE, type RestrictedAttribute, SCHEMAS, SPACE_ABOVE_ATTRIBUTE, SPACE_BELOW_ATTRIBUTE, SPECIAL_INDENT_ATTRIBUTE, STORE_ATTRIBUTE, STYLE_NAME_ATTRIBUTE, type Schema, type SchemaCode, type SchemaKind, TRACK_CHANGES_ATTRS, TRACK_CHANGES_ATTR_DATA_DATE, TRACK_CHANGES_ATTR_DATA_TIME, TRACK_CHANGES_ATTR_DATA_USERID, TRACK_CHANGES_ATTR_DATA_USERNAME, type ValidationError, type ValidationErrorKind, type ValidationErrorLevel, createElement, findAttributeDefinitionByName, findSchemaByCode, findSchemaByElement, findSchemaByTagName, getAttributeName, isAttributeRestricted, isIgnoredAttribute, isIgnoredElement, isIgnoredValidation, isTrackChangesSchema, validateXml };
package/dist/index.js CHANGED
@@ -25,7 +25,6 @@ __export(index_exports, {
25
25
  DEPENDENT_FORBIDDEN_ELEMENTS: () => DEPENDENT_FORBIDDEN_ELEMENTS,
26
26
  ELEMENT_SCHEMAS: () => ELEMENT_SCHEMAS,
27
27
  ELE_ID_ATTRIBUTE: () => ELE_ID_ATTRIBUTE,
28
- ElementLabel: () => ElementLabel,
29
28
  FIGURE_PLACEMENT_ATTRIBUTE: () => FIGURE_PLACEMENT_ATTRIBUTE,
30
29
  ID_ATTRS: () => ID_ATTRS,
31
30
  IGNORED_ATTRIBUTES: () => IGNORED_ATTRIBUTES,
@@ -56,6 +55,7 @@ __export(index_exports, {
56
55
  findAttributeDefinitionByName: () => findAttributeDefinitionByName,
57
56
  findSchemaByCode: () => findSchemaByCode,
58
57
  findSchemaByElement: () => findSchemaByElement,
58
+ findSchemaByTagName: () => findSchemaByTagName,
59
59
  getAttributeName: () => getAttributeName,
60
60
  isAttributeRestricted: () => isAttributeRestricted,
61
61
  isIgnoredAttribute: () => isIgnoredAttribute,
@@ -67,8 +67,24 @@ __export(index_exports, {
67
67
  module.exports = __toCommonJS(index_exports);
68
68
 
69
69
  // src/schema/utils/createSchema.ts
70
- var createSchema = (params) => {
71
- return params;
70
+ var createSchema = ({
71
+ code,
72
+ kind,
73
+ tag,
74
+ requiredAttributes,
75
+ optionalAttributes,
76
+ defaultChildCode,
77
+ allowedChildrenCodes
78
+ }) => {
79
+ return {
80
+ code,
81
+ kind,
82
+ tag,
83
+ requiredAttributes,
84
+ optionalAttributes,
85
+ defaultChildCode,
86
+ allowedChildrenCodes
87
+ };
72
88
  };
73
89
 
74
90
  // src/schema/shared/globalAttributes.ts
@@ -169,7 +185,6 @@ var COMMENT_QUERY_SECTION_SCHEMA = createSchema({
169
185
  kind: "atom",
170
186
  tag: SPAN_TAG,
171
187
  code: "commentQuerySection",
172
- matchesElement: (element) => element.classList.contains("cmtQurySection"),
173
188
  requiredAttributes: [...ID_ATTRS, "id"],
174
189
  optionalAttributes: [...TRACK_CHANGES_ATTRS, "title"],
175
190
  allowedChildrenCodes: [...ATOM_CODES_ALLOWED_CHILDREN]
@@ -189,7 +204,6 @@ var DELETION_SCHEMA = createSchema({
189
204
  code: "deletion",
190
205
  kind: "atom",
191
206
  tag: SPAN_TAG,
192
- matchesElement: (element) => element.classList.contains("del"),
193
207
  requiredAttributes: [...TRACK_CHANGES_ATTRS, ...ID_ATTRS],
194
208
  optionalAttributes: [],
195
209
  allowedChildrenCodes: [
@@ -240,7 +254,6 @@ var INSERTION_SCHEMA = createSchema({
240
254
  code: "insertion",
241
255
  kind: "atom",
242
256
  tag: SPAN_TAG,
243
- matchesElement: (element) => element.classList.contains("ins"),
244
257
  requiredAttributes: [...TRACK_CHANGES_ATTRS, ...ID_ATTRS],
245
258
  optionalAttributes: [],
246
259
  allowedChildrenCodes: [
@@ -1962,21 +1975,15 @@ var EPIGRAPH_SCHEMA = createSchema({
1962
1975
  });
1963
1976
 
1964
1977
  // src/schema/elementSchemas/elements/footnoteSection.ts
1965
- var FOOTNOTE_SECTION_ROLES = ["ch", "bk"];
1966
1978
  var FOOTNOTE_SECTION_SCHEMA = createSchema({
1967
1979
  code: "footnoteSection",
1968
1980
  kind: "element",
1969
1981
  tag: SECTION_TAG,
1970
- matchesElement: (element) => {
1971
- const role = element.getAttribute("role");
1972
- if (role && FOOTNOTE_SECTION_ROLES.includes(role)) return true;
1973
- return element.querySelector(FOOTNOTE_TAG) !== null;
1974
- },
1975
1982
  requiredAttributes: [
1976
1983
  ...ID_ATTRS,
1977
1984
  {
1978
1985
  name: "role",
1979
- values: FOOTNOTE_SECTION_ROLES,
1986
+ values: ["ch", "bk"],
1980
1987
  defaultValue: "ch"
1981
1988
  }
1982
1989
  ],
@@ -1989,22 +1996,16 @@ var FOOTNOTE_SECTION_SCHEMA = createSchema({
1989
1996
  });
1990
1997
 
1991
1998
  // src/schema/elementSchemas/elements/heading.ts
1992
- var HEADING_ROLES = ["H1", "H2", "H3", "H4", "H5", "H6", "vignette_group"];
1993
1999
  var HEADING_SCHEMA = createSchema({
1994
2000
  code: "heading",
1995
2001
  kind: "element",
1996
2002
  tag: SECTION_TAG,
1997
- matchesElement: (element) => {
1998
- const role = element.getAttribute("role");
1999
- if (!role) return false;
2000
- return HEADING_ROLES.includes(role);
2001
- },
2002
2003
  requiredAttributes: [
2003
2004
  ...ID_ATTRS,
2004
2005
  "xmlid",
2005
2006
  {
2006
2007
  name: "role",
2007
- values: HEADING_ROLES,
2008
+ values: ["H1", "H2", "H3", "H4", "H5", "H6", "vignette_group"],
2008
2009
  defaultValue: "H1"
2009
2010
  }
2010
2011
  ],
@@ -2072,6 +2073,7 @@ var MATHPHRASE_SCHEMA = createSchema({
2072
2073
  requiredAttributes: [ELE_ID_ATTRIBUTE],
2073
2074
  optionalAttributes: [],
2074
2075
  allowedChildrenCodes: [{ code: MATH_SCHEMA.code, required: true }]
2076
+ // TODO: Should have flag for insertion and deletion
2075
2077
  });
2076
2078
 
2077
2079
  // src/schema/elementSchemas/elements/reference/bibliodiv.ts
@@ -2369,11 +2371,42 @@ var isIgnoredAttribute = (attribute) => IGNORED_ATTRIBUTES.includes(attribute) |
2369
2371
  var isIgnoredElement = (element) => IGNORED_ELEMENT_SELECTORS.some((selector) => element.matches(selector));
2370
2372
  var isIgnoredValidation = (code) => IGNORED_VALIDATION_ELEMENT_SELECTORS.includes(code);
2371
2373
  var findSchemaByCode = (code) => SCHEMAS[code];
2374
+ var findSchemaByTagName = (tagName) => Object.values(SCHEMAS).filter(
2375
+ (schema) => schema.tag === tagName.toUpperCase()
2376
+ );
2372
2377
  var findSchemaByElement = (element) => {
2373
- const schema = Object.values(SCHEMAS).find(
2374
- (schema2) => schema2.tag == element.tagName && schema2.matchesElement?.(element)
2375
- );
2376
- return schema;
2378
+ const tagName = element.tagName.toUpperCase();
2379
+ const isInsertion = element.classList.contains("ins");
2380
+ const isDeletion = element.classList.contains("del");
2381
+ const isQueryComment = element.classList.contains("cmtQurySection");
2382
+ const role = element.getAttribute("role");
2383
+ if (tagName === SPAN_TAG) {
2384
+ if (isQueryComment) {
2385
+ return findSchemaByCode("commentQuerySection");
2386
+ }
2387
+ if (isInsertion) {
2388
+ return findSchemaByCode("insertion");
2389
+ }
2390
+ if (isDeletion) {
2391
+ return findSchemaByCode("deletion");
2392
+ }
2393
+ }
2394
+ if (tagName === SECTION_TAG) {
2395
+ if (role) {
2396
+ if (role.startsWith("H")) {
2397
+ return findSchemaByCode("heading");
2398
+ }
2399
+ if (role === "ch" || role === "bk") {
2400
+ return findSchemaByCode("footnoteSection");
2401
+ }
2402
+ }
2403
+ const isFootnoteSectionWithoutRole = element.querySelector(FOOTNOTE_TAG);
2404
+ if (isFootnoteSectionWithoutRole) {
2405
+ return findSchemaByCode("footnoteSection");
2406
+ }
2407
+ }
2408
+ const candidates = findSchemaByTagName(tagName);
2409
+ return candidates[0];
2377
2410
  };
2378
2411
  var isAttributeRestricted = (attr) => typeof attr !== "string";
2379
2412
  var getAttributeName = (attribute) => typeof attribute === "string" ? attribute.toLowerCase() : attribute.name.toLowerCase();
@@ -2387,144 +2420,50 @@ var findAttributeDefinitionByName = (definitions, name) => {
2387
2420
  };
2388
2421
 
2389
2422
  // src/schema/utils/createElement.ts
2423
+ var validateAttributes = (attributes, schema) => {
2424
+ const definitions = [
2425
+ ...schema.requiredAttributes,
2426
+ ...schema.optionalAttributes
2427
+ ];
2428
+ for (const [name, value] of Object.entries(attributes)) {
2429
+ if (value === void 0) {
2430
+ continue;
2431
+ }
2432
+ const definition = findAttributeDefinitionByName(definitions, name);
2433
+ if (!definition) {
2434
+ throw new Error(
2435
+ `Invalid attribute "${name}" for schema "${schema.code}".`
2436
+ );
2437
+ }
2438
+ if (!isAttributeRestricted(definition)) {
2439
+ continue;
2440
+ }
2441
+ if (!definition.values.includes(value)) {
2442
+ throw new Error(
2443
+ `Invalid value "${value}" for attribute "${name}" on schema "${schema.code}". Allowed values: ${definition.values.join(", ")}.`
2444
+ );
2445
+ }
2446
+ }
2447
+ };
2390
2448
  var createElement = ({
2391
2449
  schemaCode,
2392
2450
  attributes
2393
2451
  }) => {
2394
2452
  const schema = findSchemaByCode(schemaCode);
2453
+ if (!schema) {
2454
+ throw new Error(`Schema not found for code: ${schemaCode}`);
2455
+ }
2456
+ validateAttributes(attributes, schema);
2395
2457
  const element = document.createElement(schema.tag.toLowerCase());
2396
2458
  for (const [name, value] of Object.entries(attributes)) {
2397
- if (!value) continue;
2459
+ if (value === void 0) {
2460
+ continue;
2461
+ }
2398
2462
  element.setAttribute(name, String(value));
2399
2463
  }
2400
2464
  return element;
2401
2465
  };
2402
2466
 
2403
- // src/schema/utils/labelElements.ts
2404
- var ElementLabel = {
2405
- referenceBibliomixed: "Reference Bibliomixed",
2406
- referenceBibliography: "Reference Bibliography",
2407
- bibliodiv: "Bibliodiv",
2408
- indexterm: "Indexterm",
2409
- primary: "Primary",
2410
- secondary: "Secondary",
2411
- tertiary: "Tertiary",
2412
- quaternary: "Quaternary",
2413
- bookmark: "Bookmark",
2414
- see: "Cross Reference",
2415
- commentQuerySection: "Comment/Query",
2416
- comment: "Comment",
2417
- content: "Content",
2418
- query: "Query",
2419
- reply: "Reply",
2420
- deletion: "Deletion",
2421
- emphasis: "Emphasis",
2422
- highlightmark: "Highlightmark",
2423
- i: "Italic",
2424
- insertion: "Insertion",
2425
- sc: "Small Caps",
2426
- strike: "Strike",
2427
- sub: "Subscript",
2428
- sup: "Superscript",
2429
- u: "Underline",
2430
- b: "Bold",
2431
- referenceMark: "Reference Mark",
2432
- degrees: "Degrees",
2433
- email: "Email",
2434
- equation: "Equation",
2435
- fignote: "Fignote",
2436
- figsource: "Figsource",
2437
- firstname: "Firstname",
2438
- footnote: "Footnote Marker",
2439
- footnoteno: "Footnote Number",
2440
- honorific: "Honorific",
2441
- img: "Image",
2442
- informalfigure: "Floating Figure",
2443
- keyword: "Keyword",
2444
- label1: "Label",
2445
- li: "List Item",
2446
- line: "Line",
2447
- lrh: "Running Head Left",
2448
- math: "Math",
2449
- mathphrase: "Math Phrase",
2450
- merror: "Math Error",
2451
- mfrac: "Math Fraction",
2452
- mi: "Math Identifier",
2453
- mmultiscripts: "Math Multiscripts",
2454
- mn: "Math Number",
2455
- mo: "Math Operator",
2456
- mover: "Math Over",
2457
- mpadded: "Math Padded",
2458
- mphantom: "Math Phantom",
2459
- mprescripts: "Math Prescripts",
2460
- mroot: "Math Root",
2461
- mrow: "Math Row",
2462
- ms: "Math Space",
2463
- mspace: "Math Space",
2464
- msqrt: "Math Square Root",
2465
- mstyle: "Math Style",
2466
- msub: "Math Subscript",
2467
- msubsup: "Math Subscript Superscript",
2468
- msup: "Math Superscript",
2469
- mtable: "Math Table",
2470
- mtd: "Math Table Data",
2471
- mtext: "Math Text",
2472
- mtr: "Math Table Row",
2473
- munder: "Math Under",
2474
- munderover: "Math Under Over",
2475
- olink: "Online Link",
2476
- org: "Org",
2477
- orgname: "Orgname",
2478
- para: "Paragraph",
2479
- punc: "Punctuation",
2480
- rrh: "Running Head Right",
2481
- source1: "Source",
2482
- speaker: "Speaker",
2483
- subtitle1: "Subtitle",
2484
- surname: "Surname",
2485
- tag: "Tag",
2486
- tblfn: "Table Footnote",
2487
- tblsource: "Table Source",
2488
- thead: "Thead",
2489
- title1: "Title",
2490
- token: "Token",
2491
- tp: "Table Paragraph",
2492
- abstract1: "Abstract",
2493
- affiliation: "Affiliation",
2494
- alttext: "Alttext",
2495
- appendix: "Appendix",
2496
- author: "Author",
2497
- authorgroup: "Authorgroup",
2498
- blockquote: "Blockquote",
2499
- caption1: "Caption",
2500
- chapter: "Chapter",
2501
- dialogue: "Dialogue",
2502
- epigraph: "Epigraph",
2503
- figure1: "Figure",
2504
- footnoteSection: "Footnote Section",
2505
- heading: "Heading",
2506
- imageobject: "Image Object",
2507
- info1: "Info",
2508
- keywordset: "Keyword FGroup",
2509
- mediaobject: "Mediaobject",
2510
- ol: "Ordered List",
2511
- personname: "Person Name",
2512
- poem: "Poem",
2513
- sidebar: "Box",
2514
- table: "Table",
2515
- table1: "Table",
2516
- tbody: "Table Body",
2517
- td: "Table Data",
2518
- tgroup: "Table Group",
2519
- tr: "Table Row",
2520
- ul: "Unordered List",
2521
- footnote1: "Footnote",
2522
- inlineequation: "Inline Equation",
2523
- link: "Link",
2524
- uri: "URI",
2525
- phrase: "Phrase"
2526
- };
2527
-
2528
2467
  // src/schema/shared/dependentsForbiddenElements.ts
2529
2468
  var DEPENDENT_FORBIDDEN_ELEMENTS = [
2530
2469
  {
@@ -2699,7 +2638,7 @@ var validateLinkend = (linkElement, elementsToLink) => {
2699
2638
  };
2700
2639
 
2701
2640
  // src/validation/validateAttributes.ts
2702
- var validateAttributes = (element, schema) => {
2641
+ var validateAttributes2 = (element, schema) => {
2703
2642
  const errors = [];
2704
2643
  const definitions = [
2705
2644
  ...schema.requiredAttributes,
@@ -2750,7 +2689,7 @@ var validateElementAgainstSchema = (element, schema) => {
2750
2689
  description: `The element is not allowed in this context.`
2751
2690
  });
2752
2691
  }
2753
- errors.push(...validateAttributes(element, schema));
2692
+ errors.push(...validateAttributes2(element, schema));
2754
2693
  errors.push(...validateChildren(element, schema));
2755
2694
  return errors;
2756
2695
  };
@@ -2765,7 +2704,7 @@ var validateRequiredChildren = (element, schema, childElements) => {
2765
2704
  return childSchema?.code === allowedChild.code;
2766
2705
  });
2767
2706
  if (!hasChild) {
2768
- const requiredChildrenTag = findSchemaByCode(allowedChild.code).tag;
2707
+ const requiredChildrenTag = findSchemaByCode(allowedChild.code)?.tag || allowedChild.code;
2769
2708
  addValidationIssue(errors, {
2770
2709
  store: getStore(element),
2771
2710
  title: `Missing required child element ${requiredChildrenTag}`,
@@ -2930,7 +2869,6 @@ var validateXml = (xml, elementsToLink, linkElements, schemasToValidateAgainst)
2930
2869
  DEPENDENT_FORBIDDEN_ELEMENTS,
2931
2870
  ELEMENT_SCHEMAS,
2932
2871
  ELE_ID_ATTRIBUTE,
2933
- ElementLabel,
2934
2872
  FIGURE_PLACEMENT_ATTRIBUTE,
2935
2873
  ID_ATTRS,
2936
2874
  IGNORED_ATTRIBUTES,
@@ -2961,6 +2899,7 @@ var validateXml = (xml, elementsToLink, linkElements, schemasToValidateAgainst)
2961
2899
  findAttributeDefinitionByName,
2962
2900
  findSchemaByCode,
2963
2901
  findSchemaByElement,
2902
+ findSchemaByTagName,
2964
2903
  getAttributeName,
2965
2904
  isAttributeRestricted,
2966
2905
  isIgnoredAttribute,
package/dist/index.mjs CHANGED
@@ -1,6 +1,22 @@
1
1
  // src/schema/utils/createSchema.ts
2
- var createSchema = (params) => {
3
- return params;
2
+ var createSchema = ({
3
+ code,
4
+ kind,
5
+ tag,
6
+ requiredAttributes,
7
+ optionalAttributes,
8
+ defaultChildCode,
9
+ allowedChildrenCodes
10
+ }) => {
11
+ return {
12
+ code,
13
+ kind,
14
+ tag,
15
+ requiredAttributes,
16
+ optionalAttributes,
17
+ defaultChildCode,
18
+ allowedChildrenCodes
19
+ };
4
20
  };
5
21
 
6
22
  // src/schema/shared/globalAttributes.ts
@@ -101,7 +117,6 @@ var COMMENT_QUERY_SECTION_SCHEMA = createSchema({
101
117
  kind: "atom",
102
118
  tag: SPAN_TAG,
103
119
  code: "commentQuerySection",
104
- matchesElement: (element) => element.classList.contains("cmtQurySection"),
105
120
  requiredAttributes: [...ID_ATTRS, "id"],
106
121
  optionalAttributes: [...TRACK_CHANGES_ATTRS, "title"],
107
122
  allowedChildrenCodes: [...ATOM_CODES_ALLOWED_CHILDREN]
@@ -121,7 +136,6 @@ var DELETION_SCHEMA = createSchema({
121
136
  code: "deletion",
122
137
  kind: "atom",
123
138
  tag: SPAN_TAG,
124
- matchesElement: (element) => element.classList.contains("del"),
125
139
  requiredAttributes: [...TRACK_CHANGES_ATTRS, ...ID_ATTRS],
126
140
  optionalAttributes: [],
127
141
  allowedChildrenCodes: [
@@ -172,7 +186,6 @@ var INSERTION_SCHEMA = createSchema({
172
186
  code: "insertion",
173
187
  kind: "atom",
174
188
  tag: SPAN_TAG,
175
- matchesElement: (element) => element.classList.contains("ins"),
176
189
  requiredAttributes: [...TRACK_CHANGES_ATTRS, ...ID_ATTRS],
177
190
  optionalAttributes: [],
178
191
  allowedChildrenCodes: [
@@ -1894,21 +1907,15 @@ var EPIGRAPH_SCHEMA = createSchema({
1894
1907
  });
1895
1908
 
1896
1909
  // src/schema/elementSchemas/elements/footnoteSection.ts
1897
- var FOOTNOTE_SECTION_ROLES = ["ch", "bk"];
1898
1910
  var FOOTNOTE_SECTION_SCHEMA = createSchema({
1899
1911
  code: "footnoteSection",
1900
1912
  kind: "element",
1901
1913
  tag: SECTION_TAG,
1902
- matchesElement: (element) => {
1903
- const role = element.getAttribute("role");
1904
- if (role && FOOTNOTE_SECTION_ROLES.includes(role)) return true;
1905
- return element.querySelector(FOOTNOTE_TAG) !== null;
1906
- },
1907
1914
  requiredAttributes: [
1908
1915
  ...ID_ATTRS,
1909
1916
  {
1910
1917
  name: "role",
1911
- values: FOOTNOTE_SECTION_ROLES,
1918
+ values: ["ch", "bk"],
1912
1919
  defaultValue: "ch"
1913
1920
  }
1914
1921
  ],
@@ -1921,22 +1928,16 @@ var FOOTNOTE_SECTION_SCHEMA = createSchema({
1921
1928
  });
1922
1929
 
1923
1930
  // src/schema/elementSchemas/elements/heading.ts
1924
- var HEADING_ROLES = ["H1", "H2", "H3", "H4", "H5", "H6", "vignette_group"];
1925
1931
  var HEADING_SCHEMA = createSchema({
1926
1932
  code: "heading",
1927
1933
  kind: "element",
1928
1934
  tag: SECTION_TAG,
1929
- matchesElement: (element) => {
1930
- const role = element.getAttribute("role");
1931
- if (!role) return false;
1932
- return HEADING_ROLES.includes(role);
1933
- },
1934
1935
  requiredAttributes: [
1935
1936
  ...ID_ATTRS,
1936
1937
  "xmlid",
1937
1938
  {
1938
1939
  name: "role",
1939
- values: HEADING_ROLES,
1940
+ values: ["H1", "H2", "H3", "H4", "H5", "H6", "vignette_group"],
1940
1941
  defaultValue: "H1"
1941
1942
  }
1942
1943
  ],
@@ -2004,6 +2005,7 @@ var MATHPHRASE_SCHEMA = createSchema({
2004
2005
  requiredAttributes: [ELE_ID_ATTRIBUTE],
2005
2006
  optionalAttributes: [],
2006
2007
  allowedChildrenCodes: [{ code: MATH_SCHEMA.code, required: true }]
2008
+ // TODO: Should have flag for insertion and deletion
2007
2009
  });
2008
2010
 
2009
2011
  // src/schema/elementSchemas/elements/reference/bibliodiv.ts
@@ -2301,11 +2303,42 @@ var isIgnoredAttribute = (attribute) => IGNORED_ATTRIBUTES.includes(attribute) |
2301
2303
  var isIgnoredElement = (element) => IGNORED_ELEMENT_SELECTORS.some((selector) => element.matches(selector));
2302
2304
  var isIgnoredValidation = (code) => IGNORED_VALIDATION_ELEMENT_SELECTORS.includes(code);
2303
2305
  var findSchemaByCode = (code) => SCHEMAS[code];
2306
+ var findSchemaByTagName = (tagName) => Object.values(SCHEMAS).filter(
2307
+ (schema) => schema.tag === tagName.toUpperCase()
2308
+ );
2304
2309
  var findSchemaByElement = (element) => {
2305
- const schema = Object.values(SCHEMAS).find(
2306
- (schema2) => schema2.tag == element.tagName && schema2.matchesElement?.(element)
2307
- );
2308
- return schema;
2310
+ const tagName = element.tagName.toUpperCase();
2311
+ const isInsertion = element.classList.contains("ins");
2312
+ const isDeletion = element.classList.contains("del");
2313
+ const isQueryComment = element.classList.contains("cmtQurySection");
2314
+ const role = element.getAttribute("role");
2315
+ if (tagName === SPAN_TAG) {
2316
+ if (isQueryComment) {
2317
+ return findSchemaByCode("commentQuerySection");
2318
+ }
2319
+ if (isInsertion) {
2320
+ return findSchemaByCode("insertion");
2321
+ }
2322
+ if (isDeletion) {
2323
+ return findSchemaByCode("deletion");
2324
+ }
2325
+ }
2326
+ if (tagName === SECTION_TAG) {
2327
+ if (role) {
2328
+ if (role.startsWith("H")) {
2329
+ return findSchemaByCode("heading");
2330
+ }
2331
+ if (role === "ch" || role === "bk") {
2332
+ return findSchemaByCode("footnoteSection");
2333
+ }
2334
+ }
2335
+ const isFootnoteSectionWithoutRole = element.querySelector(FOOTNOTE_TAG);
2336
+ if (isFootnoteSectionWithoutRole) {
2337
+ return findSchemaByCode("footnoteSection");
2338
+ }
2339
+ }
2340
+ const candidates = findSchemaByTagName(tagName);
2341
+ return candidates[0];
2309
2342
  };
2310
2343
  var isAttributeRestricted = (attr) => typeof attr !== "string";
2311
2344
  var getAttributeName = (attribute) => typeof attribute === "string" ? attribute.toLowerCase() : attribute.name.toLowerCase();
@@ -2319,144 +2352,50 @@ var findAttributeDefinitionByName = (definitions, name) => {
2319
2352
  };
2320
2353
 
2321
2354
  // src/schema/utils/createElement.ts
2355
+ var validateAttributes = (attributes, schema) => {
2356
+ const definitions = [
2357
+ ...schema.requiredAttributes,
2358
+ ...schema.optionalAttributes
2359
+ ];
2360
+ for (const [name, value] of Object.entries(attributes)) {
2361
+ if (value === void 0) {
2362
+ continue;
2363
+ }
2364
+ const definition = findAttributeDefinitionByName(definitions, name);
2365
+ if (!definition) {
2366
+ throw new Error(
2367
+ `Invalid attribute "${name}" for schema "${schema.code}".`
2368
+ );
2369
+ }
2370
+ if (!isAttributeRestricted(definition)) {
2371
+ continue;
2372
+ }
2373
+ if (!definition.values.includes(value)) {
2374
+ throw new Error(
2375
+ `Invalid value "${value}" for attribute "${name}" on schema "${schema.code}". Allowed values: ${definition.values.join(", ")}.`
2376
+ );
2377
+ }
2378
+ }
2379
+ };
2322
2380
  var createElement = ({
2323
2381
  schemaCode,
2324
2382
  attributes
2325
2383
  }) => {
2326
2384
  const schema = findSchemaByCode(schemaCode);
2385
+ if (!schema) {
2386
+ throw new Error(`Schema not found for code: ${schemaCode}`);
2387
+ }
2388
+ validateAttributes(attributes, schema);
2327
2389
  const element = document.createElement(schema.tag.toLowerCase());
2328
2390
  for (const [name, value] of Object.entries(attributes)) {
2329
- if (!value) continue;
2391
+ if (value === void 0) {
2392
+ continue;
2393
+ }
2330
2394
  element.setAttribute(name, String(value));
2331
2395
  }
2332
2396
  return element;
2333
2397
  };
2334
2398
 
2335
- // src/schema/utils/labelElements.ts
2336
- var ElementLabel = {
2337
- referenceBibliomixed: "Reference Bibliomixed",
2338
- referenceBibliography: "Reference Bibliography",
2339
- bibliodiv: "Bibliodiv",
2340
- indexterm: "Indexterm",
2341
- primary: "Primary",
2342
- secondary: "Secondary",
2343
- tertiary: "Tertiary",
2344
- quaternary: "Quaternary",
2345
- bookmark: "Bookmark",
2346
- see: "Cross Reference",
2347
- commentQuerySection: "Comment/Query",
2348
- comment: "Comment",
2349
- content: "Content",
2350
- query: "Query",
2351
- reply: "Reply",
2352
- deletion: "Deletion",
2353
- emphasis: "Emphasis",
2354
- highlightmark: "Highlightmark",
2355
- i: "Italic",
2356
- insertion: "Insertion",
2357
- sc: "Small Caps",
2358
- strike: "Strike",
2359
- sub: "Subscript",
2360
- sup: "Superscript",
2361
- u: "Underline",
2362
- b: "Bold",
2363
- referenceMark: "Reference Mark",
2364
- degrees: "Degrees",
2365
- email: "Email",
2366
- equation: "Equation",
2367
- fignote: "Fignote",
2368
- figsource: "Figsource",
2369
- firstname: "Firstname",
2370
- footnote: "Footnote Marker",
2371
- footnoteno: "Footnote Number",
2372
- honorific: "Honorific",
2373
- img: "Image",
2374
- informalfigure: "Floating Figure",
2375
- keyword: "Keyword",
2376
- label1: "Label",
2377
- li: "List Item",
2378
- line: "Line",
2379
- lrh: "Running Head Left",
2380
- math: "Math",
2381
- mathphrase: "Math Phrase",
2382
- merror: "Math Error",
2383
- mfrac: "Math Fraction",
2384
- mi: "Math Identifier",
2385
- mmultiscripts: "Math Multiscripts",
2386
- mn: "Math Number",
2387
- mo: "Math Operator",
2388
- mover: "Math Over",
2389
- mpadded: "Math Padded",
2390
- mphantom: "Math Phantom",
2391
- mprescripts: "Math Prescripts",
2392
- mroot: "Math Root",
2393
- mrow: "Math Row",
2394
- ms: "Math Space",
2395
- mspace: "Math Space",
2396
- msqrt: "Math Square Root",
2397
- mstyle: "Math Style",
2398
- msub: "Math Subscript",
2399
- msubsup: "Math Subscript Superscript",
2400
- msup: "Math Superscript",
2401
- mtable: "Math Table",
2402
- mtd: "Math Table Data",
2403
- mtext: "Math Text",
2404
- mtr: "Math Table Row",
2405
- munder: "Math Under",
2406
- munderover: "Math Under Over",
2407
- olink: "Online Link",
2408
- org: "Org",
2409
- orgname: "Orgname",
2410
- para: "Paragraph",
2411
- punc: "Punctuation",
2412
- rrh: "Running Head Right",
2413
- source1: "Source",
2414
- speaker: "Speaker",
2415
- subtitle1: "Subtitle",
2416
- surname: "Surname",
2417
- tag: "Tag",
2418
- tblfn: "Table Footnote",
2419
- tblsource: "Table Source",
2420
- thead: "Thead",
2421
- title1: "Title",
2422
- token: "Token",
2423
- tp: "Table Paragraph",
2424
- abstract1: "Abstract",
2425
- affiliation: "Affiliation",
2426
- alttext: "Alttext",
2427
- appendix: "Appendix",
2428
- author: "Author",
2429
- authorgroup: "Authorgroup",
2430
- blockquote: "Blockquote",
2431
- caption1: "Caption",
2432
- chapter: "Chapter",
2433
- dialogue: "Dialogue",
2434
- epigraph: "Epigraph",
2435
- figure1: "Figure",
2436
- footnoteSection: "Footnote Section",
2437
- heading: "Heading",
2438
- imageobject: "Image Object",
2439
- info1: "Info",
2440
- keywordset: "Keyword FGroup",
2441
- mediaobject: "Mediaobject",
2442
- ol: "Ordered List",
2443
- personname: "Person Name",
2444
- poem: "Poem",
2445
- sidebar: "Box",
2446
- table: "Table",
2447
- table1: "Table",
2448
- tbody: "Table Body",
2449
- td: "Table Data",
2450
- tgroup: "Table Group",
2451
- tr: "Table Row",
2452
- ul: "Unordered List",
2453
- footnote1: "Footnote",
2454
- inlineequation: "Inline Equation",
2455
- link: "Link",
2456
- uri: "URI",
2457
- phrase: "Phrase"
2458
- };
2459
-
2460
2399
  // src/schema/shared/dependentsForbiddenElements.ts
2461
2400
  var DEPENDENT_FORBIDDEN_ELEMENTS = [
2462
2401
  {
@@ -2631,7 +2570,7 @@ var validateLinkend = (linkElement, elementsToLink) => {
2631
2570
  };
2632
2571
 
2633
2572
  // src/validation/validateAttributes.ts
2634
- var validateAttributes = (element, schema) => {
2573
+ var validateAttributes2 = (element, schema) => {
2635
2574
  const errors = [];
2636
2575
  const definitions = [
2637
2576
  ...schema.requiredAttributes,
@@ -2682,7 +2621,7 @@ var validateElementAgainstSchema = (element, schema) => {
2682
2621
  description: `The element is not allowed in this context.`
2683
2622
  });
2684
2623
  }
2685
- errors.push(...validateAttributes(element, schema));
2624
+ errors.push(...validateAttributes2(element, schema));
2686
2625
  errors.push(...validateChildren(element, schema));
2687
2626
  return errors;
2688
2627
  };
@@ -2697,7 +2636,7 @@ var validateRequiredChildren = (element, schema, childElements) => {
2697
2636
  return childSchema?.code === allowedChild.code;
2698
2637
  });
2699
2638
  if (!hasChild) {
2700
- const requiredChildrenTag = findSchemaByCode(allowedChild.code).tag;
2639
+ const requiredChildrenTag = findSchemaByCode(allowedChild.code)?.tag || allowedChild.code;
2701
2640
  addValidationIssue(errors, {
2702
2641
  store: getStore(element),
2703
2642
  title: `Missing required child element ${requiredChildrenTag}`,
@@ -2861,7 +2800,6 @@ export {
2861
2800
  DEPENDENT_FORBIDDEN_ELEMENTS,
2862
2801
  ELEMENT_SCHEMAS,
2863
2802
  ELE_ID_ATTRIBUTE,
2864
- ElementLabel,
2865
2803
  FIGURE_PLACEMENT_ATTRIBUTE,
2866
2804
  ID_ATTRS,
2867
2805
  IGNORED_ATTRIBUTES,
@@ -2892,6 +2830,7 @@ export {
2892
2830
  findAttributeDefinitionByName,
2893
2831
  findSchemaByCode,
2894
2832
  findSchemaByElement,
2833
+ findSchemaByTagName,
2895
2834
  getAttributeName,
2896
2835
  isAttributeRestricted,
2897
2836
  isIgnoredAttribute,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pro-editor-schema",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Pro Editor XML schemas",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -19,13 +19,18 @@
19
19
  "build": "tsup src/index.ts --format cjs,esm --dts",
20
20
  "dev": "tsup src/index.ts --format cjs,esm --dts --watch",
21
21
  "typecheck": "tsc --noEmit",
22
+ "lint": "eslint src",
22
23
  "test": "vitest run",
23
- "test:watch": "vitest"
24
+ "test:watch": "vitest",
25
+ "check": "npm ci && npm run typecheck && npm run lint && npm run test && npm run build"
24
26
  },
25
27
  "devDependencies": {
28
+ "@eslint/js": "^10.0.1",
29
+ "eslint": "^10.6.0",
26
30
  "happy-dom": "^20.0.10",
27
31
  "tsup": "^8.0.0",
28
32
  "typescript": "^5.0.0",
33
+ "typescript-eslint": "^8.62.1",
29
34
  "vitest": "^3.2.4"
30
35
  }
31
36
  }