@miy2/xml-api 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +21 -0
- package/README.md +94 -0
- package/dist/collab/bridge.d.ts +13 -0
- package/dist/collab/bridge.js +2 -0
- package/dist/cst/grammar.d.ts +93 -0
- package/dist/cst/grammar.js +95 -0
- package/dist/cst/parser.d.ts +23 -0
- package/dist/cst/parser.js +161 -0
- package/dist/cst/xml-cst.d.ts +88 -0
- package/dist/cst/xml-cst.js +116 -0
- package/dist/cst/xml-grammar.d.ts +45 -0
- package/dist/cst/xml-grammar.js +366 -0
- package/dist/dom.d.ts +172 -0
- package/dist/dom.js +415 -0
- package/dist/engine/editor-state.d.ts +22 -0
- package/dist/engine/editor-state.js +32 -0
- package/dist/engine/sync-engine.d.ts +55 -0
- package/dist/engine/sync-engine.js +401 -0
- package/dist/engine/transaction.d.ts +30 -0
- package/dist/engine/transaction.js +52 -0
- package/dist/history-manager.d.ts +23 -0
- package/dist/history-manager.js +40 -0
- package/dist/model/formatter.d.ts +20 -0
- package/dist/model/formatter.js +112 -0
- package/dist/model/xml-api-model.d.ts +47 -0
- package/dist/model/xml-api-model.js +125 -0
- package/dist/model/xml-binder.d.ts +37 -0
- package/dist/model/xml-binder.js +484 -0
- package/dist/model/xml-schema.d.ts +9 -0
- package/dist/model/xml-schema.js +16 -0
- package/dist/xml-api-events.d.ts +43 -0
- package/dist/xml-api-events.js +29 -0
- package/dist/xml-api.d.ts +62 -0
- package/dist/xml-api.js +152 -0
- package/package.json +64 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.XMLSchema = void 0;
|
|
4
|
+
class XMLSchema {
|
|
5
|
+
constructor(definitions = []) {
|
|
6
|
+
this.elements = new Map();
|
|
7
|
+
for (const def of definitions) {
|
|
8
|
+
this.elements.set(def.name, def);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
isVoid(tagName) {
|
|
12
|
+
var _a, _b;
|
|
13
|
+
return (_b = (_a = this.elements.get(tagName)) === null || _a === void 0 ? void 0 : _a.isVoid) !== null && _b !== void 0 ? _b : false;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
exports.XMLSchema = XMLSchema;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { Transaction } from "./engine/transaction";
|
|
2
|
+
import type { ModelNode } from "./model/xml-api-model";
|
|
3
|
+
/**
|
|
4
|
+
* Event object emitted when the XML model changes.
|
|
5
|
+
*/
|
|
6
|
+
export type ChangeEvent = {
|
|
7
|
+
type: "full";
|
|
8
|
+
target?: ModelNode;
|
|
9
|
+
transaction?: Transaction;
|
|
10
|
+
} | {
|
|
11
|
+
type: "structure";
|
|
12
|
+
target: ModelNode;
|
|
13
|
+
transaction?: Transaction;
|
|
14
|
+
} | {
|
|
15
|
+
type: "attribute";
|
|
16
|
+
target: ModelNode;
|
|
17
|
+
key: string;
|
|
18
|
+
newValue: string | null;
|
|
19
|
+
transaction?: Transaction;
|
|
20
|
+
} | {
|
|
21
|
+
type: "text";
|
|
22
|
+
target: ModelNode;
|
|
23
|
+
newValue?: string;
|
|
24
|
+
transaction?: Transaction;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Callback function for handling change events.
|
|
28
|
+
*/
|
|
29
|
+
export type EventHandler = (event: ChangeEvent) => void;
|
|
30
|
+
/**
|
|
31
|
+
* Internal event emitter for managing listeners.
|
|
32
|
+
*/
|
|
33
|
+
export declare class EventEmitter {
|
|
34
|
+
private listeners;
|
|
35
|
+
/**
|
|
36
|
+
* Registers an event handler.
|
|
37
|
+
* @param handler The callback function.
|
|
38
|
+
* @returns A function to unsubscribe the handler.
|
|
39
|
+
*/
|
|
40
|
+
on(handler: EventHandler): () => void;
|
|
41
|
+
off(handler: EventHandler): void;
|
|
42
|
+
emit(event: ChangeEvent): void;
|
|
43
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EventEmitter = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Internal event emitter for managing listeners.
|
|
6
|
+
*/
|
|
7
|
+
class EventEmitter {
|
|
8
|
+
constructor() {
|
|
9
|
+
this.listeners = [];
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Registers an event handler.
|
|
13
|
+
* @param handler The callback function.
|
|
14
|
+
* @returns A function to unsubscribe the handler.
|
|
15
|
+
*/
|
|
16
|
+
on(handler) {
|
|
17
|
+
this.listeners.push(handler);
|
|
18
|
+
return () => this.off(handler);
|
|
19
|
+
}
|
|
20
|
+
off(handler) {
|
|
21
|
+
this.listeners = this.listeners.filter((h) => h !== handler);
|
|
22
|
+
}
|
|
23
|
+
emit(event) {
|
|
24
|
+
for (const handler of this.listeners) {
|
|
25
|
+
handler(event);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
exports.EventEmitter = EventEmitter;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { Document } from "./dom";
|
|
2
|
+
import type { Grammar } from "./cst/grammar";
|
|
3
|
+
import type { CST } from "./cst/xml-cst";
|
|
4
|
+
import { ModelElement, type ModelNode } from "./model/xml-api-model";
|
|
5
|
+
import { type EventHandler } from "./xml-api-events";
|
|
6
|
+
/**
|
|
7
|
+
* The primary entry point for the XML API.
|
|
8
|
+
* Orchestrates the synchronization between source code (CST) and the logical Model.
|
|
9
|
+
*/
|
|
10
|
+
export declare class XMLAPI {
|
|
11
|
+
private engine;
|
|
12
|
+
private document;
|
|
13
|
+
/**
|
|
14
|
+
* Initializes the API with the source XML string.
|
|
15
|
+
* @param source The initial XML source code.
|
|
16
|
+
* @param grammar (Optional) Custom grammar definition.
|
|
17
|
+
*/
|
|
18
|
+
constructor(source: string, grammar?: Grammar);
|
|
19
|
+
/** The current source code string. */
|
|
20
|
+
get source(): string;
|
|
21
|
+
/**
|
|
22
|
+
* Alias for `source` to maintain compatibility with existing tests/demos temporarily.
|
|
23
|
+
* @deprecated Use `source` instead.
|
|
24
|
+
*/
|
|
25
|
+
get input(): string;
|
|
26
|
+
/** The authoritative logical model. */
|
|
27
|
+
get model(): ModelElement | null;
|
|
28
|
+
/** The Concrete Syntax Tree (Physical layer). */
|
|
29
|
+
get cst(): CST | null;
|
|
30
|
+
/** The grammar used for parsing. */
|
|
31
|
+
get grammar(): Grammar;
|
|
32
|
+
/**
|
|
33
|
+
* Updates the source code directly (e.g. from a text editor).
|
|
34
|
+
* Attempts an optimized incremental update, falling back to full re-parse if needed.
|
|
35
|
+
* @param from Start index of the range to replace.
|
|
36
|
+
* @param to End index of the range.
|
|
37
|
+
* @param text The new text to insert.
|
|
38
|
+
*/
|
|
39
|
+
updateSource(from: number, to: number, text: string): void;
|
|
40
|
+
/**
|
|
41
|
+
* Alias for `updateSource` to maintain compatibility.
|
|
42
|
+
* @deprecated Use `updateSource` instead.
|
|
43
|
+
*/
|
|
44
|
+
updateInput(from: number, to: number, text: string): void;
|
|
45
|
+
/**
|
|
46
|
+
* Returns a DOM-compatible Document object linked to this API.
|
|
47
|
+
* Changes made to the returned Document are automatically reflected in the source code.
|
|
48
|
+
*/
|
|
49
|
+
getDocument(): Document;
|
|
50
|
+
/**
|
|
51
|
+
* Registers an event handler to listen for model changes.
|
|
52
|
+
*/
|
|
53
|
+
on(handler: EventHandler): () => void;
|
|
54
|
+
undo(): void;
|
|
55
|
+
redo(): void;
|
|
56
|
+
/** @deprecated Use DOM interface or Engine directly if needed. */
|
|
57
|
+
setAttribute(modelNode: ModelElement, key: string, value: string): void;
|
|
58
|
+
/** @deprecated Use DOM interface or Engine directly if needed. */
|
|
59
|
+
updateText(modelNode: ModelElement, text: string): void;
|
|
60
|
+
/** @deprecated Use DOM interface or Engine directly if needed. */
|
|
61
|
+
replaceNode(target: ModelNode, content: ModelNode): void;
|
|
62
|
+
}
|
package/dist/xml-api.js
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.XMLAPI = void 0;
|
|
4
|
+
const dom_1 = require("./dom");
|
|
5
|
+
const sync_engine_1 = require("./engine/sync-engine");
|
|
6
|
+
const xml_api_model_1 = require("./model/xml-api-model");
|
|
7
|
+
/**
|
|
8
|
+
* The primary entry point for the XML API.
|
|
9
|
+
* Orchestrates the synchronization between source code (CST) and the logical Model.
|
|
10
|
+
*/
|
|
11
|
+
class XMLAPI {
|
|
12
|
+
/**
|
|
13
|
+
* Initializes the API with the source XML string.
|
|
14
|
+
* @param source The initial XML source code.
|
|
15
|
+
* @param grammar (Optional) Custom grammar definition.
|
|
16
|
+
*/
|
|
17
|
+
constructor(source, grammar) {
|
|
18
|
+
this.document = null;
|
|
19
|
+
this.engine = new sync_engine_1.SyncEngine(source, grammar);
|
|
20
|
+
}
|
|
21
|
+
// --- Read-only State Access ---
|
|
22
|
+
/** The current source code string. */
|
|
23
|
+
get source() {
|
|
24
|
+
return this.engine.source;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Alias for `source` to maintain compatibility with existing tests/demos temporarily.
|
|
28
|
+
* @deprecated Use `source` instead.
|
|
29
|
+
*/
|
|
30
|
+
get input() {
|
|
31
|
+
return this.engine.source;
|
|
32
|
+
}
|
|
33
|
+
/** The authoritative logical model. */
|
|
34
|
+
get model() {
|
|
35
|
+
return this.engine.model;
|
|
36
|
+
}
|
|
37
|
+
/** The Concrete Syntax Tree (Physical layer). */
|
|
38
|
+
get cst() {
|
|
39
|
+
return this.engine.cst;
|
|
40
|
+
}
|
|
41
|
+
/** The grammar used for parsing. */
|
|
42
|
+
get grammar() {
|
|
43
|
+
return this.engine.grammar;
|
|
44
|
+
}
|
|
45
|
+
// --- Operations ---
|
|
46
|
+
/**
|
|
47
|
+
* Updates the source code directly (e.g. from a text editor).
|
|
48
|
+
* Attempts an optimized incremental update, falling back to full re-parse if needed.
|
|
49
|
+
* @param from Start index of the range to replace.
|
|
50
|
+
* @param to End index of the range.
|
|
51
|
+
* @param text The new text to insert.
|
|
52
|
+
*/
|
|
53
|
+
updateSource(from, to, text) {
|
|
54
|
+
this.engine.updateSource(from, to, text);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Alias for `updateSource` to maintain compatibility.
|
|
58
|
+
* @deprecated Use `updateSource` instead.
|
|
59
|
+
*/
|
|
60
|
+
updateInput(from, to, text) {
|
|
61
|
+
this.updateSource(from, to, text);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Returns a DOM-compatible Document object linked to this API.
|
|
65
|
+
* Changes made to the returned Document are automatically reflected in the source code.
|
|
66
|
+
*/
|
|
67
|
+
getDocument() {
|
|
68
|
+
if (this.document)
|
|
69
|
+
return this.document;
|
|
70
|
+
const doc = new dom_1.Document();
|
|
71
|
+
this.document = doc;
|
|
72
|
+
if (this.model) {
|
|
73
|
+
const rootWrapper = (0, dom_1.createWrapper)(this.model, doc);
|
|
74
|
+
if (rootWrapper instanceof dom_1.Element) {
|
|
75
|
+
doc.documentElement = rootWrapper;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// Set up observer to sync DOM changes back to Source via Engine
|
|
79
|
+
doc.setObserver({
|
|
80
|
+
onAttributeChange: (element, name, value) => {
|
|
81
|
+
const model = element.getModel();
|
|
82
|
+
if (model instanceof xml_api_model_1.ModelElement) {
|
|
83
|
+
if (value === null) {
|
|
84
|
+
// Attribute removal support needed in Engine/Binder
|
|
85
|
+
console.warn("Attribute removal not fully supported yet");
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
this.engine.setAttribute(model, name, value);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
onTextChange: (node, text) => {
|
|
93
|
+
// Direct text update on CharacterData
|
|
94
|
+
// Need to find parent element to update properly or support direct node replacement
|
|
95
|
+
// For now, simple text node update via parent if available
|
|
96
|
+
// But Engine expects ModelElement for updateText.
|
|
97
|
+
// Actually, replacing the node is better.
|
|
98
|
+
// Or implement updateTextNode in Engine.
|
|
99
|
+
console.warn("Direct CharacterData update not yet supported via Observer");
|
|
100
|
+
},
|
|
101
|
+
onElementTextChange: (element, text) => {
|
|
102
|
+
const model = element.getModel();
|
|
103
|
+
if (model instanceof xml_api_model_1.ModelElement) {
|
|
104
|
+
this.engine.updateText(model, text);
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
onChildAdded: (parent, child, index) => {
|
|
108
|
+
const parentModel = parent.getModel();
|
|
109
|
+
const childModel = child.getModel();
|
|
110
|
+
if (parentModel instanceof xml_api_model_1.ModelElement) {
|
|
111
|
+
this.engine.insertNode(parentModel, childModel, index);
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
onChildRemoved: (parent, child, index) => {
|
|
115
|
+
const parentModel = parent.getModel();
|
|
116
|
+
const childModel = child.getModel();
|
|
117
|
+
if (parentModel instanceof xml_api_model_1.ModelElement) {
|
|
118
|
+
this.engine.removeNode(parentModel, childModel);
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
return doc;
|
|
123
|
+
}
|
|
124
|
+
// --- Subscriptions ---
|
|
125
|
+
/**
|
|
126
|
+
* Registers an event handler to listen for model changes.
|
|
127
|
+
*/
|
|
128
|
+
on(handler) {
|
|
129
|
+
return this.engine.on(handler);
|
|
130
|
+
}
|
|
131
|
+
// --- History ---
|
|
132
|
+
undo() {
|
|
133
|
+
this.engine.undo();
|
|
134
|
+
}
|
|
135
|
+
redo() {
|
|
136
|
+
this.engine.redo();
|
|
137
|
+
}
|
|
138
|
+
// --- Advanced/Legacy Operation Shortcuts (Proxy to Engine) ---
|
|
139
|
+
/** @deprecated Use DOM interface or Engine directly if needed. */
|
|
140
|
+
setAttribute(modelNode, key, value) {
|
|
141
|
+
this.engine.setAttribute(modelNode, key, value);
|
|
142
|
+
}
|
|
143
|
+
/** @deprecated Use DOM interface or Engine directly if needed. */
|
|
144
|
+
updateText(modelNode, text) {
|
|
145
|
+
this.engine.updateText(modelNode, text);
|
|
146
|
+
}
|
|
147
|
+
/** @deprecated Use DOM interface or Engine directly if needed. */
|
|
148
|
+
replaceNode(target, content) {
|
|
149
|
+
this.engine.replaceNode(target, content);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
exports.XMLAPI = XMLAPI;
|
package/package.json
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@miy2/xml-api",
|
|
3
|
+
"version": "0.9.0",
|
|
4
|
+
"description": "An XML library that faithfully synchronizes documents and AST.",
|
|
5
|
+
"main": "dist/xml-api.js",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"types": "./dist/xml-api.d.ts",
|
|
9
|
+
"default": "./dist/xml-api.js"
|
|
10
|
+
},
|
|
11
|
+
"./*": {
|
|
12
|
+
"types": "./dist/*.d.ts",
|
|
13
|
+
"default": "./dist/*.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"types": "dist/xml-api.d.ts",
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"access": "public"
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist"
|
|
22
|
+
],
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "tsc",
|
|
25
|
+
"prepublishOnly": "npm run build",
|
|
26
|
+
"typecheck": "tsc --noEmit",
|
|
27
|
+
"docs:typecheck": "vue-tsc --noEmit -p docs/tsconfig.json",
|
|
28
|
+
"test": "jest",
|
|
29
|
+
"test:all": "jest",
|
|
30
|
+
"docs:dev": "vitepress dev docs",
|
|
31
|
+
"docs:build": "vitepress build docs",
|
|
32
|
+
"docs:gen-api": "typedoc && ts-node scripts/generate-sidebar.ts",
|
|
33
|
+
"docs:preview": "vitepress preview docs",
|
|
34
|
+
"format": "biome format --write ."
|
|
35
|
+
},
|
|
36
|
+
"keywords": [
|
|
37
|
+
"xml",
|
|
38
|
+
"ast",
|
|
39
|
+
"code",
|
|
40
|
+
"parser"
|
|
41
|
+
],
|
|
42
|
+
"author": "Kazuya Miyashita <miy1924@gmail.com> (https://miy2.com/)",
|
|
43
|
+
"license": "MIT",
|
|
44
|
+
"packageManager": "pnpm@10.23.0",
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@biomejs/biome": "2.3.13",
|
|
47
|
+
"@types/jest": "^30.0.0",
|
|
48
|
+
"@types/jsdom": "^27.0.0",
|
|
49
|
+
"@types/node": "^25.0.10",
|
|
50
|
+
"esbuild": "^0.27.2",
|
|
51
|
+
"jest": "^30.2.0",
|
|
52
|
+
"jsdom": "^27.4.0",
|
|
53
|
+
"mermaid": "^11.12.2",
|
|
54
|
+
"ts-jest": "^29.4.6",
|
|
55
|
+
"ts-node": "^10.9.2",
|
|
56
|
+
"typedoc": "^0.28.16",
|
|
57
|
+
"typedoc-plugin-markdown": "^4.9.0",
|
|
58
|
+
"typescript": "^5.9.3",
|
|
59
|
+
"vitepress": "^1.6.4",
|
|
60
|
+
"vitepress-plugin-mermaid": "^2.0.17",
|
|
61
|
+
"vue": "^3.5.27",
|
|
62
|
+
"vue-tsc": "^3.2.4"
|
|
63
|
+
}
|
|
64
|
+
}
|